中禁止异常信息传递到析构函数外牛

检测设备2021年09月06日

在有两种情况下会调用析构函数。第一种是在正常情况下删除一个对象,例如对象超出了作用域或被显式地delete。第二种是异常传递的堆栈辗转开解(stack-unwinding)过程中,由异常处理系统删除一个对象。

在上述两种情况下,调用析构函数时异常可能处于激活状态也可能没有处于激活状态。遗憾的是没有办法在析构函数内部区分出这两种情况。因此在写析构函数时你必须保守地假设有异常被激活,因为如果在一个异常被激活的同时,析构函数也抛出异常,并导致程序控制权转移到析构函数外,C++将调用terminate函数。这个函数的作用正如其名字所表示的:它终止你程序的运行,而且是立即终止,甚至连局部对象都没有被释放。

下面举一个例子,一个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 (...) { }

}

catch表面上好像没有做任何事情,这是一个假象,实际上它阻止了任何从logDestruction抛出的异常被传递到session析构函数的外面。我们现在能高枕无忧了,无论session对象是不是在堆栈辗转开解(stack unwinding)中被释放,terminate函数都不会被调用。

不允许异常传递到析构函数外面还有第二个原因。如果一个异常被析构函数抛出而没有在函数内部捕获住,那么析构函数就不会完全运行(它会停在抛出异常的那个地方上)。如果析构函数不完全运行,它就无法完成希望它做的所有事情。例如,我们对session类做一个修改,在建立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 ,特别是“Destructors That Throw and Why They’re Evil”这段)。查看本文来源

昆明治疗白癜风好方法
太原皮肤科专治医院
天津包皮包茎治疗费用
相关阅读
恭喜!千万粉丝网红“小刚学长”当爸,亲自帮妻子按摩不必要血栓

对于身为百万粉丝网成员的小马上,同窗来说,五四绝对是一个值得庆祝的往...

2024-09-05
职场情商课:掌控“现像效应”,发挥积极作用,遵从自己的内心

本文看点:惯常effect是心理学之外的专业术语,又指乐队烟火effect,又叫“随...

2024-07-27
热门 抱怨是最浪费力气且没用的事情。 内心强大,是抗击一切痛苦的前提。 活着比什么都重要,活在当下。

热门 抱怨是最浪费力气且就让的事情。 内心强盛,是抵御一切病痛的前提。...

2024-06-29
公职考试也看“家庭背景”,这3类人很有优势,考上机会很大!

公详的垄断压力却是是一年比一年大,每年省详、国详都是几十上百万人报上...

2024-06-23
【手慢无】Plus会员专享47元 京东自营帅气竞走雨衣

2022-06-23 09:18:04 所作:张军 随着夏末暴雨的预示,打工人出门需要更高的随...

2024-05-05
你曾经羡慕的人,在起初都貌似完美无缺

你以前喜欢的人,在先前都貌似完美无缺。即便当他逐渐四分五裂转化成一堆...

2024-02-23
友情链接