C++中禁止异常传递到析构函数外面有两个原因:1)异常转递的堆栈辗转开解的过程中,防止terminate函数被调用;2)能够帮助确保析构函数总能完成我们希望它完成的动作。
C++中有两种情况下会调用析构函数:1)正常情况下删除对象,如对象超出作用域或者对象指针被显示delete掉;
2)异常传递的堆栈辗转开解(stack-unwinding)过程中,由异常处理系统删除一个对象。
在上述两种情况下,调用析构函数时候异常可能处于激活状态也可能处于未激活状态,但是编译器却无法对其进行区分,这将使得我们的程序很脆弱。如果一个异常被激活的同时,同时析构函数也抛出了异常,这时候就会非常恐怖,程序的控制权转移到析构函数的外部,C++直接会调用terminate函数,并且会理解终止程序的运行,甚至局部对象都没有被释放。
1、一个异常被激活同时析构函数抛出异常,这将导致terminate函数立即执行:
class Session{
public:
Session();
~Session();
...
private:
static void logCreation(Session* objAddr);
static void logDestruction(Session* objAddr);
};
现在我们调用logDestruction()函数:
Session::~Session(){
logDestruction(this);
}
试想一下如果在调用logDestruction()函数的过程中抛出异常,同时该异常也将在析构函数中抛出,这感觉,是不是感觉爽歪歪哈~~~
当然有同学会说我们可以这样处理呀:调用try…catch…异常处理机制进行局部处理:
Session::~Session(){
try{
logDestruction(this);
}catch(...){
cerr<<"ERROR at address"<<this<<".\n";
}
}
但是请注意这儿针对opertor<<运算符仍旧可能导致异常被抛出,又回到原来的老问题了。。。
所以我们的推荐解决方法如下:
Session::~Session(){
try{
logDestruction(this);
}catch(...){}
}
如果异常被析构函数抛出而没有在函数内部被不捕获,析构函数就完全不会运行,将停在抛出异常的那个地方上。
Session::Session(){
logCreation(this);
startTransaction();
}
Session::~Session(){
logDestruction(this);
endTransaction();
}
如果在这里logDestruction抛出一个异常,在session构造函数内启动的transaction就没有终止。我们也许能够通过重新调整session析构函数内的函数调用顺序来消除问题,但是如果endTransaction也抛出异常的话,我们除了使用try…catch…之外别无它法。