zoukankan      html  css  js  c++  java
  • 第67课 经典问题解析五

    1. 指针的判别:判断一个变量是不是指针

    (1)拾遗

      ①C++中仍然支持C语言中的可变参数函数

      ②C++编译器的匹配调用优先级重载函数>函数模板>变参函数

    (2)思路

      ①将变量分为两类指针 VS 非指针

      ②编写函数:

      • 指针变量调用时返回true

      • 非指针变量调用时返回false

    (3)函数模板变参函数化学反应

    template<typename T>  //优先匹配函数模板
    bool IsPtr(T* v)      // match pointer
    {
    
        return true;
    
    }
    
    
    //变参函数            //再匹配变参函数
    bool IsPtr(...)     // match non-pointer
    {
      
        return false;
    
    }

    【编程实验】指针判断  67-1.cpp

     方案1:利用函数模板变参函数优先级

    #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;
    
    }

    运行结果: 

      

    方案2:直接利用函数模板的重载

    #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;
    
    }

    运行结果:

      

    (4)存在缺陷及完善

      ①变参函数无法解析自定义类类型的对象参数可能造成程序崩溃

      ②可以在编译期就精确匹配结果,而不需要等到实际调用IsPtr时才确定思路是通过sizeof,具体见例子中的注释。

    2. 构造函数中的异常

    (1)当构造函数抛出异常时:

      ①构造函数立即停止

      ②当前对象无法生成

      ③析构函数不会被调用

      ④对象所占用的空间立即收回

    (2)工程项目中的建议

      ①不要在构造函数中抛出异常(注:也可以在构造函数中try-catch可能的异常,并在异常发生时做善后的处理(如资源释放),最后再将这个异常抛出,以通知外部的函数。但建议不要这样做,因为有更好的二阶构造模式,二阶模式在内部自己处理了异常,而抛异常的方法是把异常处理再次丢给了外部的函数

      ②当构造函数可能产生异常时,使用二阶构造模式

    【编程实验】构造中的异常  67-2.cpp

    #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下可以用如下命令检查是否内存泄漏

    g++ -g test.cpp

    valgrind --tool=memcheck --leak-check=full ./a.out

    /*输出结果:(注意Test的析构函数并没有被调用!)

    Test()

    Exception...

    p = 0x1

    */

    3. 析构函数中的异常

    (1)析构函数抛出异常导致:对象所使用资源无法完全释放

    (2)避免在析构函数中抛出异常

    4. 小结

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

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

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

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

  • 相关阅读:
    静态函数的访问修饰符的问题
    DropDownList 控件控制Image控件动态显示图像
    使用网页对话框来显示图片 window.open()
    用Response.Redirect()方法进行页面的传值
    XML DTD文档定义语法汇总
    json字符串片段转换成HTML片段字符串的问题
    细数改善WPF应用程序性能的10大方法(转)
    C#递归算法-遍历XML文件,以UL列表显示树形结构目录
    我的博客园
    CSDN,让我暴得好彻底......
  • 原文地址:https://www.cnblogs.com/hoiday/p/10211801.html
Copyright © 2011-2022 走看看