大多数人说永远不要从析构函数抛出异常——这样做会导致未定义的行为。Stroustrup指出,“vector析构函数显式地为每个元素调用析构函数。这意味着如果元素析构函数抛出,则vector销毁失败…实际上没有很好的方法来防止析构函数抛出异常,因此标准库不保证元素析构函数是否抛出”(摘自附录E3.2)。
这篇文章似乎不是这么说的——抛出析构函数或多或少是可以的。
所以我的问题是,如果从析构函数抛出导致未定义的行为,你如何处理析构函数期间发生的错误?
如果在清理操作期间发生错误,您会忽略它吗?如果它是一个可以在堆栈中处理但不能在析构函数中处理的错误,那么从析构函数抛出异常难道没有意义吗?
显然,这种错误很少见,但也有可能发生。
问:所以我的问题是——如果
从析构函数抛出会导致
未定义的行为,你该如何处理
在析构函数期间发生的错误?
A:有几种选择:
让异常流出析构函数,而不管其他地方发生了什么。在这样做的时候,要意识到(甚至害怕)std::terminate可能会随之而来。
永远不要让异常从析构函数流出。可能是写一个日志,一些大红色坏文本,如果可以的话。
我的最爱:如果std::uncaught_exception返回false,让你的异常流出。如果返回true,则退回到日志记录方法。
但加进d'tors好吗?
我同意上面的大部分观点,在析构函数中最好避免抛出,因为它可以在析构函数中抛出。但有时你最好接受它的发生,并妥善处理。我选择上面的3。
在一些奇怪的情况下,从析构函数抛出实际上是个好主意。
比如“必须检查”错误代码。这是一个从函数返回的值类型。如果调用者读取/检查包含的错误代码,返回值将静默销毁。
但是,如果返回值超出作用域时还没有读取返回的错误代码,则它将从析构函数抛出一些异常。
这很危险,但从可读性/代码可理解性的角度来看,这也没有意义。
你要问的是在这种情况下
int foo()
{
Object o;
// As foo exits, o's destructor is called
}
什么应该捕获异常?foo的调用者应该这样做吗?或者应该由foo来处理它?为什么foo的调用者要关心foo内部的某个对象?可能有一种语言定义的方式是有意义的,但它将是不可读的和难以理解的。
更重要的是,对象的内存去哪里了?对象拥有的内存到哪里去了?它仍然被分配吗(表面上是因为析构函数失败了)?考虑到对象在堆栈空间中,所以它显然已经消失了。
然后考虑这个例子
class Object
{
Object2 obj2;
Object3* obj3;
virtual ~Object()
{
// What should happen when this fails? How would I actually destroy this?
delete obj3;
// obj 2 fails to destruct when it goes out of scope, now what!?!?
// should the exception propogate?
}
};
当obj3的删除失败时,我如何以一种保证不会失败的方式删除?该死的是我的记忆!
现在考虑在第一个代码片段中,Object自动消失,因为它在堆栈上,而Object3在堆上。因为指向Object3的指针没有了,你就有点SOL了,你有内存泄漏。
下面是一种安全的做法
class Socket
{
virtual ~Socket()
{
try
{
Close();
}
catch (...)
{
// Why did close fail? make sure it *really* does close here
}
}
};
也可以参考这个FAQ