大多数人说永远不要从析构函数抛出异常——这样做会导致未定义的行为。Stroustrup指出,“vector析构函数显式地为每个元素调用析构函数。这意味着如果元素析构函数抛出,则vector销毁失败…实际上没有很好的方法来防止析构函数抛出异常,因此标准库不保证元素析构函数是否抛出”(摘自附录E3.2)。

这篇文章似乎不是这么说的——抛出析构函数或多或少是可以的。

所以我的问题是,如果从析构函数抛出导致未定义的行为,你如何处理析构函数期间发生的错误?

如果在清理操作期间发生错误,您会忽略它吗?如果它是一个可以在堆栈中处理但不能在析构函数中处理的错误,那么从析构函数抛出异常难道没有意义吗?

显然,这种错误很少见,但也有可能发生。


当前回答

关于从析构函数抛出,真正要问自己的问题是“调用者可以用它做什么?”你是否真的可以对异常做一些有用的事情,来抵消从析构函数抛出的危险?

如果我销毁了一个Foo对象,而Foo析构函数抛出了一个异常,我可以合理地对它做什么?我可以记录,也可以忽略。这是所有。我不能“修复”它,因为Foo对象已经消失了。最好的情况是,我记录异常并继续,就像什么都没有发生一样(或者终止程序)。这真的值得通过从析构函数抛出来潜在地引起未定义的行为吗?

其他回答

与构造函数不同,在构造函数中抛出异常是指示对象创建成功的有用方法,而在析构函数中不应抛出异常。

当在堆栈展开过程中从析构函数抛出异常时,就会发生问题。如果发生这种情况,编译器将处于不知道是继续堆栈展开过程还是处理新异常的情况。最终的结果是,您的程序将立即终止。

因此,最好的做法就是完全避免在析构函数中使用异常。相反,将消息写入日志文件。

设置告警事件。通常,警报事件是在清理对象时通知失败的更好形式

Throwing out of a destructor can result in a crash, because this destructor might be called as part of "Stack unwinding". Stack unwinding is a procedure which takes place when an exception is thrown. In this procedure, all the objects that were pushed into the stack since the "try" and until the exception was thrown, will be terminated -> their destructors will be called. And during this procedure, another exception throw is not allowed, because it's not possible to handle two exceptions at a time, thus, this will provoke a call to abort(), the program will crash and the control will return to the OS.

问:所以我的问题是——如果 从析构函数抛出会导致 未定义的行为,你该如何处理 在析构函数期间发生的错误?

A:有几种选择:

让异常流出析构函数,而不管其他地方发生了什么。在这样做的时候,要意识到(甚至害怕)std::terminate可能会随之而来。 永远不要让异常从析构函数流出。可能是写一个日志,一些大红色坏文本,如果可以的话。 我的最爱:如果std::uncaught_exception返回false,让你的异常流出。如果返回true,则退回到日志记录方法。

但加进d'tors好吗?

我同意上面的大部分观点,在析构函数中最好避免抛出,因为它可以在析构函数中抛出。但有时你最好接受它的发生,并妥善处理。我选择上面的3。

在一些奇怪的情况下,从析构函数抛出实际上是个好主意。 比如“必须检查”错误代码。这是一个从函数返回的值类型。如果调用者读取/检查包含的错误代码,返回值将静默销毁。 但是,如果返回值超出作用域时还没有读取返回的错误代码,则它将从析构函数抛出一些异常。

你的析构函数可能在其他析构函数链中执行。抛出未被立即调用者捕获的异常可能会使多个对象处于不一致的状态,从而导致更多的问题,而不是在清理操作中忽略错误。