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