首页 | 编程语言 | 网站建设 | 游戏天堂 | 冲浪宝典 | 网络安全 | 操作系统 | 软件时空 | 硬件指南 | 病毒相关 | IT 认证
软讯网络 > 编程语言 > C/C++ > More Effective C++ 条款11:禁止异常信息(exceptions)传递到析构函数外
【标  题】:More Effective C++ 条款11:禁止异常信息(exceptions)传递到析构函数外
【关键字】:More,Effective,C++,11,exceptions
【来  源】:http://blog.csdn.net/wang_junjie/archive/2006/09/07/1189375.aspx

More Effective C++ 条款11:禁止异常信息(exceptions)传递到析构函数外

在有两种情况下会调用析构函数。第一种是在正常情况下删除一个对象,例如对象超出
了作用域或被显式地delete。第二种是异常传递的堆栈辗转开解(stack-unwinding)过
程中,由异常处理系统删除一个对象。
在上述两种情况下,调用析构函数时异常可能处于激活状态也可能没有处于激活状态。
遗憾的是没有办法在析构函数内部区分出这两种情况。因此在写析构函数时你必须保守
地假设有异常被激活,因为如果在一个异常被激活的同时,析构函数也抛出异常,并导
致程序控制权转移到析构函数外,C++将调用terminate函数。这个函数的作用正如其名
字所表示的:它终止你程序的运行,而且是立即终止,甚至连局部对象都没有被释放。
下面举一个例子,一个Session类用来跟踪在线计算机的sessions,session就是运行在
从你一登录计算机开始一直到注销出系统为止的这段期间的某种东西。每个Session对象
关注的是它建立与释放的日期与时间:
class Session {
public:
Session();
~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 (...) { }
}
catch表面上好像没有做任何事情,这是一个假象,实际上它阻止了任何从logDestruct
ion抛出的异常被传递到session析构函数的外面。我们现在能高枕无忧了,无论sessio
n对象是不是在 栈辗转开解(stack unwinding)中被释放,terminate函数都不会被调
用。
不允许异常传递到析构函数外面还有第二个原因。如果一个异常被析构函数抛出而没有
在函数内部捕获住,那么析构函数就不会完全运行(它会停在抛出异常的那个地方上)
。如果析构函数不完全运行,它就无法完成希望它做的所有事情。例如,我们对sessio
n类做一个修改,在建立session时启动一个数据库事务(database transaction),终止
session时结束这个事务:
Session::Session() // 为了简单起见,,
{ // 这个构造函数没有
// 处理异常
logCreation(this);
startTransaction(); // 启动 database transaction
}
Session::~Session()
{
logDestruction(this);
endTransaction(); // 结束database transaction
}
如果在这里logDestruction抛出一个异常,在session构造函数内启动的transaction就
没有被终止。我们也许能够通过重新调整session析构函数内的函数调用顺序来消除问题
,但是如果endTransaction也抛出一个异常,我们除了回到使用try和catch外,别无选
择。
综上所述,我们知道禁止异常传递到析构函数外有两个原因,第一能够在异常转递的堆
栈辗转开解(stack-unwinding)的过程中,防止terminate被调用。第二它能帮助确保
析构函数总能完成我们希望它做的所有事情。(如果你仍旧不很信服我所说的理由,可
以去看Herb Sutter的文章Exception-Safe Generic Containers ,特别是“Destructo
rs That Throw and Why They’re Evil”这段)。
 
More effective C++ 条款12:理解“抛出一个异常”与“传递一个参数”或“调用一个虚函数”间的差异:【上一篇】
More effective c++ 条款10:在构造函数中防止资源泄漏(下):【下一篇】
【相关文章】
  • More effective C++ 条款12:理解“抛出一个异常”与“传递一个参数”或“调用一个虚函数”间的差异
  • C++ 题内话两则 ( Copy Constructor & Initialization when object as a field)
  • Java与C++多态性浅析
  • 强大免费的C++跨平台图像处理库 ImageStone V2.0 发布
  • 读书笔记《Programming in C++》之一
  • vc++6.0STL中std::string类导致程序崩溃的解决方案
  • 怎样使用C++回调函数
  • More effective C++ 条款13:通过引用(reference)捕获异常
  • C与C++中标准输入实现方式上的一点区别
  • C++ 面试题 三
  • 【随机文章】
  • MSN Messenger 无法登陆的彻底解决方法
  • 正则表达式regular expression详述
  • 防火墙 ipfw
  • photoshop立体物件综合实例(目录)
  • 系统的安全检查
  • 将HTML表单数据存储为XML格式 - 1
  • OSPF(一):Neighbor&Adjacency
  • 在JSP中处理虚拟路径
  • C traps and pitfalls(1)
  • Microsoft Visual Studio 2007 September CTP Released!
  • 【相关评论】
    没有相关评论
    【发表评论】
    姓名:
    邮件:
    随机码*
    评论*
          
    |  首 页  |  版权声明  |  联系我们   |  网站地图  |
    CopyRight © 2004-2007 软讯网络 All Rigths Reserved.