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


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

    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)、构造函数和析构函数不要抛出异常

  • 相关阅读:
    常用设计模式
    文件上传相关报错: The current request is not a multipart request或is a MultipartResolver configured?
    Intellij IDEA 与 Gitlab 实现代码上传与下载
    Oracle两表关联,只取B表的第一条记录
    notepad++ 调整行间距
    Ubuntu18.04直接安装python3.7或者升级自带的python3.6版本之后导致终端无法打开的解决办法
    黑苹果之DELL台式机安装Mac OS X 10.13.6版本操作系统
    Windows Ping | Tracert 's Bat 脚本并行测试
    centos 7 修改 sshd | 禁止 root 登录及 sshd 端口脚本定义
    C语言中malloc函数的理解
  • 原文地址:https://www.cnblogs.com/gui-lin/p/6384638.html
Copyright © 2020-2023  润新知