一。异常
1.栈展开(stack unwinding):
异常抛出的时候,将暂停当前函数的执行,开始查找匹配的catch字句。
首先检查throw语句是否在当前函数的try语句中,如果是,而且有相应的catch语句与抛出对象相匹配,则就处理异常;如果没有在try语句中,或没有相匹配的catch语句,则就会退出当前函数(释放当前函数的内存、撤销局部对象);
然后再在调用函数中查找,即查看抛出异常的函数是否在调用函数中的try语句中,如果是且有相应的catch语句与抛出的对象匹配,则就处理异常;如果没有在try语句中, 或没有相匹配的catch语句,则会推出该调用函数;
然后再在上一层的调用函数中查找...
直到找到一个跟该异常相匹配的catch语句为止,则其处理该异常;如果异常没有被任何一个catch语句匹配,也即是不能被处理,则C++会调用terminate函数,结束整个程序。
2.析构函数:
析构函数被调用的情况有两种:
一是正常情况下删除或释放一个完全构造的对象,比如对象超出了作用域或被delete;
二是异常被抛出和传递的过程中,栈展开机制撤销一个完全构造的对象。
注意:栈展开机制只能撤销局部对象,即调用完全构造的局部对象的析构函数来释放对象;而不能也不会释放局部指针指向的动态堆内存。
3.绝对不能让异常逃出析构函数之外:
也即是如果析构函数有可能抛出异常,则必须在析构函数内消化处理所有可能的异常,原因有二:
析构函数不能完全执行析构函数,这可能会导致内存泄露;
如果一个析构函数是在异常传递的栈展开过程中被调用,而且它也抛出一个异常逃出析构函数,则会引发严重的后果:C++调用terminate函数,整个程序会立即结束,甚至来不及释放局部对象。
4.对象的完全构造:
当且仅当构造函数完全执行时,这个对象才是完全构造的对象;
C++仅能删除完全构造的对象。
5.异常可以逃出构造函数之外:
异常可以逃出构造函数之外,但是如果这种情况下不会调用这个未完全构造对象的析构函数;
而只是栈展开机制会释放局部对象(即调用成员变量的析构函数),而不会释放指针指向的堆内存,因此需要用对象管理资源。
二。虚函数
1.虚函数与析构函数:
具有多态性质的基类的析构函数必须是虚函数,这是因为:
如果基类的指针或引用指向或引用派生类对象时,当通过该指针或引用释放该对象时,则只会使用基类的析构函数,释放掉基类的部分。
但是析构函数中尽量不要调用虚函数,这是因为:
一旦开始调用派生类析构函数,则派生类的数据成员变得无定义;进入基类的析构函数后,对象便成为了一个基类对象了。
2.”虚“构造函数:
没有虚构造函数。
构造函数中也尽量不要调用虚函数,这是因为:
在派生类对象的构造函数执行之前,先执行基类的构造函数,在此期间对象的类型是基类类型。