第六十七课、经典问题解析五

一、问题一:编写一个函数判断一个变量是不是指针

1、拾遗

(1)、c++中仍然支持C语言中的可变参数函数

(2)、c++编译器的匹配调用优先级:重载函数-------->函数模板--------->变参函数

2、思路

(1)、将变量分为两类:指针VS非指针

(2)、编写函数

A、指针变量调用返回true

B、非指针变量调用时返回false

3、函数模板与变参函数的化学反应

template<typename T>  //优先匹配函数模板

bool IsPtr(T* v) // match pointer
{
    return true;
}

//变参函数            //再匹配变参函数
bool IsPtr(...)  // match non-pointer
{
    return false;

4、存在的缺陷:变参函数无法解析对象参数,可能造成程序崩溃

5、进一步挑战:如何让编译器精确匹配函数,但不实际调用

#include <iostream>
#include <string>

using namespace std;

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

//模板函数-优先匹配
template <typename T>
char IsPtr(T* v) //match pointer
{
    return ‘d‘; //返回值为:char型
}

//变参函数-匹配的优先级比模板函数低
//注意返回值与模板函数不同。这是因为
//当向变参函数中传入自定义的类类型,而出现
int IsPtr(...)  //match non-pointer
{
    return 0;   //返回值为int型。
}

//定义这个宏的主要目的是,为了传自定义类型给变参函数时,可能出现的上述问题,
//可以利用sizeof来判断返回值的大小。如果为1表示char型,为匹配了模板函数,
//为4表示int型,匹配到了变参函数,从而区别变量到底是指针还是非指针类型,注
//意巧妙地利用了sizeof编译期就能确定的特性,从而避开运行期的错误。
#define ISPTR(p) (sizeof(IsPtr(p)) == sizeof(char))

int main(int argc, char *argv[])
{
    int i = 0;
    int* p = &i;

    cout << "p is a pointer:" << ISPTR(p) << endl;   //true
    cout << "i is a pointer:" << ISPTR(i) << endl;   //false;

    Test t;
    Test* pt = &t;
    cout << "pt is a pointer:" << ISPTR(pt) << endl;   //true
    cout << "t is a pointer:" << ISPTR(t) << endl;   //false; 

    //如果直接调用IsPtr来判断自定义类类型里,可能出现:
    //error: cannot pass objects of non-trivially-copyable type
    //‘class Test‘ through ‘...‘的错误。
    //以下是trivially copyable type类型的定义:
    //1.要么全部定义了拷贝/移动/赋值函数,要么全部没定义;
    //2.没有虚成员;
    //3.基类或其它任何非static成员都是trivally copyable。
    //典型的内置类型bool、int等属于trivally copyable
    //cout << "t is a pointer:" << IsPtr(t) << endl;   //false;
    return 0;
}

方法二:重载函数模板

#include <iostream>
#include <string>

using namespace std;

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

//模板函数
template <typename T>
bool IsPtr(T* v) //match pointer
{
    return true;
}
//重载模板函数
template <typename T>
bool IsPtr(T v) //match non pointer
{
    return false;
}

int main(int argc, char *argv[])
{
    int i = 0;
    int* p = &i;

    cout << "p is a pointer:" << IsPtr(p) << endl;   //true
    cout << "i is a pointer:" << IsPtr(i) << endl;   //false;

    Test t;
    Test* pt = &t;
    cout << "pt is a pointer:" << IsPtr(pt) << endl;   //true
    cout << "t is a pointer:" << IsPtr(t) << endl;   //false; 

    return 0;
}

二、构造函数与析构函数中的异常

1、构造函数中抛出异常时

(1)、构造过程立即停止

(2)、当前对象无法生成

(3)、析构函数不会被调用

(4)、对象所占用的空间立即回收

2、工程项目中的建议

(1)、不要在构造函数中抛出异常

(2)、当构造函数可能出现异常时,使用二阶构造函数

3、避免在析构函数中抛出异常

(1)、析构函数中的异常将导致:对象所使用的资源无法完全被释放

#include <iostream>
#include <string>

using namespace std;

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

    virtual ~Test()
    {
        cout << "~Test()" << endl;
    }
};

int main(int argc, char *argv[])
{
    //将指针p指向0x00000001,目的是为了后面
    //的验证。
    Test* p = reinterpret_cast<Test*>(1);

    try
    {
        p = new Test();
    }
    catch(...)
    {
        cout << "Exception..." << endl;
    }

    cout << "p = " << p << endl; //p = 0x1

    return 0;
}
//linux下可以用如下命令检查是否内存泄漏
//  valgrind --tool=memcheck --leak-check=full ./a.out
/*输出结果:(注意Test的析构函数并没有被调用!)
Test()
Exception...
p = 0x1
*/

三、小结

(1)、c++中依然支持变参函数

(2)、变参函数无法很好处理对象参数

(3)、利用函数模板和变参函数能够判断指针变量

(4)、构造函数和析构函数中不要抛出异常

时间: 2024-10-07 07:05:21

第六十七课、经典问题解析五的相关文章

第67课 经典问题解析五

1. 指针的判别 (1)拾遗 ①C++中仍然支持C语言中的可变参数函数 ②C++编译器的匹配调用优先级:重载函数>函数模板>变参函数 (2)思路 ①将变量分为两类:指针 VS 非指针 ②编写函数: 指针变量调用时回true 非指针变量调用时返回false (3)函数模板与变参函数的化学反应 template<typename T> //优先匹配函数模板 bool IsPtr(T* v) // match pointer { return true; } //变参函数 //再匹配变参

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

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

第三十七课、深度解析QMap与QHash

一.QMap深度解析 1.QMap是一个以升序键顺序存储键值对的数据结构 (1)QMap原型为class QMap<K, T>模板 (2).QMap中的键值对根据key进行了排序 (3).QMap中的key类型必须重载operator <     (小于操作符) 2.QMap使用实例一 3.QMap使用实例二 4.QMap的注意事项 (1).通过key获取Value时 A.当key存在,返回对应的Value B.当key不存在,返回值类型所对应的"零"值 (2).插入

经典问题解析五

c+++中任然支持c语言中的可变参数函数 c++编译器的匹配调用优先级 1.重载函数 2.函数模板 3.变参函数 存在的缺陷: 变参函数无法解析对象参数,可能造成程序崩溃 进一步的挑战: 如何让编译器精确匹配函数,但不进行实际的调用?(sizeof) 构造中的异常 构造函数抛出异常 构造过程立即停止 当前对象无法生成 析构函数不会被调用 对象所占用的空间立即收回 工程项目中的建议 不要在构造函数中抛出异常 当构造函数可能产生异常时,使用二阶构造模式 避免在析构函数中抛出异常!! 析构函数的异常将

第55课 经典问题解析(四)

1. 关于动态内存分配 (1)new和malloc的区别 区别 new malloc 是否是关键字 关键字,是C++的一部分.被所有的C++编译器支持. 由C库提供的函数,注意是函数,而不是关键字,要依赖C库(cstdlib),在某些系统开发(如嵌入式)中可能不能调用. 分配单位 以具体类型为单位 以字节为单位 内存初始化 申请时可进行初始化 仅根据需要申请定量的内存,但并不进行初始化. 是否触发构造函数的调用 会触发.对象的创建只能使用new 不会,仅分配需要的内存空间,因此不适合面向对象开发

第24课 经典问题解析二

关于析构的疑问: 当程序中存在多个对象的时候,如何确定这些对象的析构顺序? 多个对象析构时,析构顺序与构造顺序相反. 假设构造三个对象a.b.c,则程序结束时,析构顺序为c.b.a. 程序示例: 1 #include <stdio.h> 2 3 class Member 4 { 5 const char* ms; 6 public: 7 Member(const char* s) 8 { 9 printf("Member(const char* s): %s\n", s);

C++--第24课 - 专题四经典问题解析

第24课 - 专题四经典问题解析 1. 历史的痕迹 #include <cstdlib> #include <iostream> using namespace std; template<class T>  //以前是用typename定义,现在是用class定义 T Minus(T a, T b) { return a - b; } template<class T>  //类模板 class Add { public: T add(T a, T b)

C++--第14课 - 专题二经典问题解析

第14课 - 专题二经典问题解析 1. malloc与free和new与delete有什么区别? malloc和free是函数,new和delete是关键字. #include <cstdlib> #include <iostream> using namespace std; class Test { private: int i; public: Test() { cout<<"Test()"<<endl; i = 0; } Test

Linux学习第六节课-用户、组合权限

Linux学习第六节课 ---------------------------------------------------------------------------------------------------------------------------------------------------------------- 三十四.安全3A 认证Authentication 授权Authorization 审计Accounting 三十五.用户和组的配置文件 /etc/pas