C++中禁止非常信息传送到析构函数外[VC/C++编程]
本文“C++中禁止非常信息传送到析构函数外[VC/C++编程]”是由七道奇为您精心收集,来源于网络转载,文章版权归文章作者所有,本站不对其观点以及内容做任何评价,请读者自行判断,以下是其具体内容:
在有两种情形下会调用析构函数.第一种是在正常情形下删除一个对象,比方对象超越了作用域或被显式地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
<< ".
";
}
}
但是这样做并不比你本来的代码安全.假如在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”这段).
以上是“C++中禁止非常信息传送到析构函数外[VC/C++编程]”的内容,如果你对以上该文章内容感兴趣,你可以看看七道奇为您推荐以下文章:
本文地址: | 与您的QQ/BBS好友分享! |