经典问题解析五(五十五)

在面试中有可能会遇到这个面试题,编写程序判断一个变量是不是指针。我们咋一看是不是有点懵逼,我们可以想到利用 C 语言中的可变参数函数。在 C++ 中依然是支持的,C++ 编译器的匹配调用优先级是:1、重载函数;2、函数模板;3、变参函数。我们可以将变量分为两类:指针和非指针。需要编写函数的功能是当是指针变量调用时便返回 true,是非指针变量调用时返回 false。

下来我们就来试着编写下这个函数

#include <iostream>
#include <string>

using namespace std;

template
< typename T >
bool IsPtr(T* v)
{
    return true;
}

bool IsPtr(...)
{
    return false;
}

int main()
{
    int i = 0;
    int* p = &i;
    
    cout << "p is a pointer: " << IsPtr(p) << endl;
    cout << "i is a pointer: " << IsPtr(i) << endl;

    return 0;
}

我们利用函数模板和可变参数函数来实现,下来看看编译结果是不是我们所期望的

我们看到已经实现了,于是满意的交给了面试官。面试官看了下,笑着说你这个程序对一般的数据类型是可行的,对于类类型还是进行判断嘛?我们接着来试下类类型的判断是否还可行,在程序中添加一个类,再生成一个类对象 t,指向类对象 t 的指针 pt,下来看看编译结果

我们看到编译直接报错了,也就是说对于类对象来说并不行,变参函数无法解析对象参数。那么我们想想怎么办呢,既然不能直接 IsPtr 函数的调用,我们还可以利用它的返回值类型的大小来进行判断,将模板函数的返回值类型设置为 char,返回一个字符;将全局函数的返回值类型设置为 int,直接返回 0。再定义一个宏用来判断函数 IsPtr 的返回值是不是等于 char  类型的大小,如果是则返回 1,否则返回 0。我们来看看程序

#include <iostream>
#include <string>

using namespace std;

class Test
{
public:
    Test()
    {
    }
    virtual ~Test()
    {
    }
};

template
< typename T >
char IsPtr(T* v)
{
    return 'c';
}

int IsPtr(...)
{
    return 0;
}

#define ISPTR(p) (sizeof(IsPtr(p)) == sizeof(char))

int main()
{
    int i = 0;
    int* p = &i;
    
    cout << "p is a pointer: " << ISPTR(p) << endl;
    cout << "i is a pointer: " << ISPTR(i) << endl;
    
    cout << endl;
    
    Test t;
    Test* pt = &t;
    
    cout << "pt is a pointer: " << ISPTR(pt) << endl;
    cout << "t is a pointer: " << ISPTR(t) << endl;

    return 0;
}

我们再次编译看看结果

我们看到已经编译通过了,并且也正确进行类对象类型的判断了。那么这个面试题我们就完美的进行回答了。还有一个面试题:如果在构造函数中抛出异常会发生什么?这便综合考查到了我们的基础知识了,涉及到对象的构造、异常以及其他方面的知识。那么在构造函数中抛出异常,最直接的影响就是构造过程会立即停止,那么当前的对象便无法生成了。由于是异常,析构函数同样也无法被调用了,对象所占用的空间会立即收回。那么在工程项目中的建议是:不要在构造函数中抛出异常,当构造函数可能产生异常时,我们便要使用二阶构造模式。

下来我们还是以代码为例来进行分析

#include <iostream>
#include <string>

using namespace std;

class Test
{
public:
    Test()
    {
        cout << "Test()" << endl;
        
        throw 0;
    }
    virtual ~Test()
    {
        cout << "~Test()" << endl;
    }
};

int main()
{
    Test* p = reinterpret_cast<Test*>(1);
    
    try
    {
        p = new Test();
    }
    catch(...)
    {
        cout << "Exception..." << endl;
    }
    
    cout << "p = " << p << endl;

    return 0;
}

我们在构造函数先打印函数名,在进行异常的抛出。先将指针 p 指向地址为 1 处,如果对象生成,那么便会返回一个地址值。我们来看看编译结果

在抛出异常后,我们看到 p 的地址还是为 1,证明并没有对象的生成。我们应避免在析构函数中抛出异常!!析构函数的异常将导致对象所使用的资源无法完全释放。通过对一些经典问题的学习,总结如下:1、C++ 中依然支持变参函数;2、变参函数无法很好的处理对象参数;3、利用函数模板和变参函数能够判断指针变量;4、构造函数和析构函数中不要抛出异常。

欢迎大家一起来学习 C++ 语言,可以加我QQ:243343083。

原文地址:http://blog.51cto.com/12810168/2125283

时间: 2024-08-13 21:28:08

经典问题解析五(五十五)的相关文章

android源码解析之(十五)--&gt;Activity销毁流程

继续我们的源码解析,上一篇文章我们介绍了Activity的启动流程,一个典型的场景就是Activity a 启动了一个Activity b,他们的生命周期回调方法是: onPause(a) –> onCreate(b) –> onStart(b) –> onResume(b) –> onStop(a) 而我们根据源码也验证了这样的生命周期调用序列,那么Activity的销毁流程呢?它的生命周期的调用顺序又是这样的呢? 这里我们我做一个简单的demo,让一个Activity a启动A

android源码解析(二十五)--&gt;onLowMemory执行流程

上篇文章中我们分析了Activity的onSaveInstanceState方法执行时机,知道了Activity在一般情况下,若只是执行onPause方法则不会执行onSaveInstanceState方法,而一旦执行了onStop方法就会执行onSaveInstanceState方法,具体的信息,可以参见onSaveInstanceState方法执行时机:http://blog.csdn.net/qq_23547831/article/details/51464535 这篇文章中同样的我们分析

上班的第五百一十五天

最近服务器出现了一点问题,就是在开服的头一两天,人数较多时会导致登陆不上. 基于我处于空闲状态,这个任务就肯定是落在我头上了. 接到这个任务,我就在想,登陆不上,而且还没提示,肯定就是网关服务器的问题.接着便把可能发生的问题罗列了一下: 1.配置没有配好,导致人数超过了上限: 2.网关服务器负载均衡部分写得有问题,导致人数多了就奔溃了: 3.服务器物理机有问题,开的服数多,导致内存不够用: 4.网络线路抽风. 针对这几个问题,我问了运维一些问题,以及把出问题的log要过来看.首先,先确定物理机的

经典问题解析二(十九)

今天我们来探讨下当程序中存在多个对象时,如何确定这些对象的析构顺序?那么单个对象创建时构造函数的调用顺序是:a> 调用父类的构造过程(我们会在后面进行讲解):b> 调用成员变量的构造函数(调用顺序与生命顺序相同):c> 调用类自身的构造函数.析构函数与对应构造函数的调用顺序相反.当多个对象析构时,析构顺序与构造顺序相反. 下来我们以代码为例进行说明 #include <stdio.h> class Member {     const char* ms; public:   

十五.jQuery源码解析之Sizzle总体结构.htm

Sizzle是一款纯javascript实现的css选择器引擎,它具有完全独立,无库依赖;小;易于扩展和兼容性好等特点. W3C Selectors API规范定义了方法querySelector()和querySelectorAll(),但是IE6,7不支持这两个方法. 在Sizzele内部,如果浏览器支持方法querySelectorAll(),则调用该方法查找元素,如果不支持,则模拟该方法的行为. Sizzle支持几乎所有的css3选择器,并且会按照文档位置返回结果. 上面截取的只是Siz

QT开发(四十五)——XML文件解析总结

QT开发(四十五)--XML文件解析总结 一.XML文件解析方式的特点 1.DOM解析XML文档的特点 基于DOM的解析器的核心是在内存中建立和XML文档相对应的树状结构.XML文件的标记.标记中的文本数据和实体等都是内存中的树状结构的某个节点相对应. 优点:可以方便地操作内存中的树状节点 缺点:如果XML文件较大,或者只需要解析XML文档的一部分数据,就会占用大量的内存空间 2.SAX解析XML文档的特点 SAX解析的核心是事件处理机制,SAX采用事件机制的方式来解析XML文档.使用SAX解析

经典算法题每日演练——第二十五题 块状链表

原文:经典算法题每日演练--第二十五题 块状链表 在数据结构的世界里,我们会认识各种各样的数据结构,每一种数据结构都能解决相应领域的问题,每一种数据结构都像 是降龙十八掌中的某一掌,掌掌毙命... 当然每个数据结构,有他的优点,必然就有它的缺点,那么如何创造一种数据结构 来将某两种数据结构进行扬长避短,那就非常完美了.这样的数据结构也有很多,比如:双端队列,还有就是今天讲的 块状链表, 我们都知道 数组 具有 O(1)的查询时间,O(N)的删除,O(N)的插入... 链表 具有 O(N)的查询时

经典算法题每日演练——第十五题 并查集

原文:经典算法题每日演练--第十五题 并查集 这一篇我们看看经典又神奇的并查集,顾名思义就是并起来查,可用于处理一些不相交集合的秒杀. 一:场景 有时候我们会遇到这样的场景,比如:M={1,4,6,8},N={2,4,5,7},我的需求就是判断{1,2}是否属于同一个集合,当然实现方法 有很多,一般情况下,普通青年会做出O(MN)的复杂度,那么有没有更轻量级的复杂度呢?嘿嘿,并查集就是用来解决这个问题的. 二:操作 从名字可以出来,并查集其实只有两种操作,并(Union)和查(Find),并查集

JavaScript学习总结(十五)——Function类

在JavaScript中,函数其实是对象,每个函数都是Function类的实例,既然函数对象,那么就具有自己的属性和方法,因此,函数名实际上也是一个指向函数对象的指针,不会与某个函数绑定. 一.函数的声明 方式一:常规方式 1 function sum1(num1,num2){ 2 return num1+num2 3 } 方式二:函数表达式 1 var sum2=function(num1,num2){ 2 return num1+num2; 3 }; 方式三:动态创建函数(这种方式用得不多)

How Javascript works (Javascript工作原理) (十五) 类和继承及 Babel 和 TypeScript 代码转换探秘

个人总结:读完这篇文章需要15分钟,文章主要讲解了Babel和TypeScript的工作原理,(例如对es6 类的转换,是将原始es6代码转换为es5代码,这些代码中包含着类似于 _classCallCheck 和 _createClass这样的函数,而这些函数已经在Babel和TypeScript的标准库中预先定义好了,然后进行处理). 顺便温习了Object.create这个方法,  比如有一个obj:{name:'是ho',f:function(){alert(1)}} var a = O