More Effective C++----(11)禁止异常信息传递到析构函数外
来源:互联网 发布:出入无时 莫知其乡解释 编辑:程序博客网 时间:2024/05/19 19:55
Item M11:禁止异常信息(exceptions)传递到析构函数外
转载请注明出处<http://blog.csdn.net/qianqin_2014/article/details/51326615>
在有两种情况下会调用析构函数。第一种是在正常情况下删除一个对象,例如对象超出了作用域或被显式地delete。第二种是异常传递的堆栈辗转开解(stack-unwinding)过程中,由异常处理系统删除一个对象。
在上述两种情况下,调用析构函数时异常可能处于激活状态也可能没有处于激活状态。遗憾的是没有办法在析构函数内部区分出这两种情况。因此在写析构函数时你必须保守地假设有异常被激活。因为如果在一个异常被激活的同时,析构函数也抛出异常,并导致程序控制权转移到析构函数外,C++将调用terminate函数。这个函数的作用正如其名字所表示的:它终止你程序的运行,而且是立即终止,甚至连局部对象都没有被释放。(这就是为什么析构函数默认为noexcept的形式的原因)
下面举一个例子,一个Session类用来跟踪在线计算机的sessions,session就是运行在从你一登录计算机开始一直到注销出系统为止的这段期间的某种东西。每个Session对象关注的是它建立与释放的日期和时间:
class Session {public: Session(); ~Session(); ... private: static void logCreation(Session *objAddr); static void logDestruction(Session *objAddr);};
函数logCreation 和 logDestruction被分别用于记录对象的建立与释放。我们因此可以这样编写Session的析构函数:
Session::~Session(){ logDestruction(this);}
一切看上去很好,但是如果logDestruction抛出一个异常,会发生什么事呢?异常没有被Session的析构函数捕获住,所以它被传递到析构函数的调用者那里。但是如果析构函数本身的调用就是源自于某些其它异常的抛出,那么terminate函数将被自动调用,彻底终止你的程序。这不是你所希望发生的事情。程序没有记录下释放对象的信息,这是不幸的,甚至是一个大麻烦。那么事态果真严重到了必须终止程序运行的地步了么?如果没有,你必须防止在logDestruction内抛出的异常传递到Session析构函数的外面。唯一的方法是用try和catch blocks。一种很自然的做法会这样编写函数:
Session::~Session(){ try { logDestruction(this); } catch (...) { cerr << "Unable to log destruction of Session object " << "at address " << this << ".\n"; }}
但是这样做并不比你原来的代码安全。如果在catch中调用operator<<时导致一个异常被抛出,我们就又遇到了老问题,一个异常被转递到Session析构函数的外面。
我们可以在catch中放入try,但是这总得有一个限度,否则会陷入循环。因此我们在释放Session时必须忽略掉所有它抛出的异常:
Session::~Session(){ try { logDestruction(this); } catch (...) { }}
不允许异常传递到析构函数外面还有第二个原因。如果一个异常被析构函数抛出而没有在函数内部捕获住,那么析构函数就不会完全运行(它会停在抛出异常的那个地方上)。如果析构函数不完全运行,它就无法完成希望它做的所有事情。例如,我们对session类做一个修改,在建立session时启动一个数据库事务(database transaction),终止session时结束这个事务:
Session::Session() // 为了简单起见,,{ // 这个构造函数没有 // 处理异常 logCreation(this); startTransaction(); // 启动 database transaction} Session::~Session(){ logDestruction(this); endTransaction(); // 结束database transaction}
综上所述,我们知道禁止异常传递到析构函数外有两个原因,第一能够在异常转递的堆栈辗转开解(stack-unwinding)的过程中,防止terminate被调用。第二它能帮助确保析构函数总能完成我们希望它做的所有事情。(如果你仍旧不很信服我所说的理由,可以去看Herb Sutter的文章Exception-Safe Generic Containers ,特别是“Destructors That Throw and Why They’re Evil”这段)。
总结:也就是说析构函数在调用其他函数释放资源时,必须确保其他的函数不要抛出异常,原因有以上两点,常用的方法就是用try和catch捕获"其他的函数"所抛出的所有异常。
0 0
- More Effective C++----(11)禁止异常信息传递到析构函数外
- C++之禁止异常信息传递到析构函数外面(10)---《More Effective C++》
- More Effective C++之Item M11:禁止异常信息(exceptions)传递到析构函数外
- C++中禁止异常信息传递到析构函数外
- C++中禁止异常信息传递到析构函数外
- C++中理解“抛出一个异常”与“传递一个参数”或“调用一个虚函数”间的差异(11)---《More Effective C++》
- More Effective C++----(12)理解"抛出一个异常"与"传递一个参数"或"调用一个虚函数"间的差异
- 《More Effective C++》读书笔记-异常
- More effective c++ 11(禁止异常(exceptions)流出destructors之外)
- More effective c++ 11(禁止异常(exceptions)流出destructors之外)
- More Effective C++:通过引用捕获异常
- More Effective C++:通过引用捕获异常
- <More Effective C++>笔记--异常
- <<More Effective C++>>读书笔记3: 异常
- More Effective C++----异常 & (9)使用析构函数防止资源泄漏
- More Effective C++:避免缺省构造函数
- More Effective(三)异常
- More Effective C++:通过引用捕获异常(转)
- 315. Count of Smaller Numbers After Self
- 数组的选择排序和冒泡排序
- Jetty启动
- 类与对象
- 剑指offer之面试题29:数组中出现次数超过一半的数字
- More Effective C++----(11)禁止异常信息传递到析构函数外
- c++ 内存分区
- 百度地图API之覆盖物和事件
- 有理有条地绘制立体图(利用数据结构)
- 关于javascript的seal的扩展性一
- iPhone手机数据提取分析(一)
- oracle dba create view 失败 解决办法
- Postgresql 9 管理 手册(第二版)
- KairosDB 安装与入门