条款8:别让异常逃离析构函数
析构函数的作用就是清理资源等后续工作,一旦这里发生异常就会产生严重后果。所以在编写析构函数时要格外小心。
如
class Widget{
public:
...
~Widget(){...}
};
void doSomething()
{
std::vector<Widget> v;
...
}
注:vector是STL内的东东,类似于数组,不过更加强大。
那么vector v被销毁时,里面的多个Widget对象也要被销毁。会多次调用~Widget(),如果一旦有连续~Widget()发生异常,那么后续的Widget就会发生内存泄露。所以针对析构函数的设计要小心。那么处理异常我第一个想到的方法就是 try catch。在析构函数本体内利用try catch 语句处理异常,还要注意析构函数内的复旦不要过大,减少发生异常概率,做到以上两点就可以尽量避免以上问题。
条款9:绝不在构造和析构过程中调用virtual函数
这个很奇怪,反正在见到这句话之前,我还真没想过这么做(因为没有见到过,不敢用)。如下
class Transaction{
public:
Transaction();
virtual void logTransaction() const=0;
....
};
尽管我没这么做过,很多人都没这么做过,但是不排除以下可能性的发生
class Transaction{
public:
Transaction()
{init();}
virtual void logTransaction() const=0;
...
private:
void init()
{
...
logTransaction();
}
};
以上两种行为都是不允许的。为何?
若有子类,且构造对象
class BuyTransaction:public Transaction{
public:
virtual void logTransaction() const;
...
};
BuyTransaction b;
那么我们知道在子类的构造期间的顺序是先构造基类部分(即调用基类构造函数)然后再构造子类。在构造基类时,基类构造函数中的虚拟函数就会运行,并且其还是基类版本。为什么呢?因为在构造基类时,子类的的特有成员还都不存在,此时logTransaction()必定是基类版本。由于纯虚函数,必定会发生错误。即使不是pure virtual,那也可能违背了使用者的原意。同理析构函数析构顺序相反,但结果也是这样。