C++之禁止异常信息传递到析构函数外面(10)---《More Effective C++》

来源:互联网 发布:sqlserver删除实例 编辑:程序博客网 时间:2024/06/06 12:46

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…之外别无它法。

阅读全文
0 0