我知道在析构函数中不应该抛出异常。
如果我的析构函数调用的函数可能会抛出异常,那么在析构函数中捕获并不将其继续抛出是否可以?或者它仍然可能导致程序中止,因此我根本不应该从析构函数中调用这样的函数?
是的,这是合法的。一个异常不能从析构函数中“逃逸”,但是在析构函数中或其调用的函数中发生的任何事情都由你决定。
(从技术上讲,异常也可以从析构函数调用中“逃逸”。如果这发生在堆栈展开期间,因为另一个异常被抛出,std::terminate
会被调用。因此,按照标准来说这是定义良好的,但这是一个非常糟糕的想法。)
是的。
看一下标准库中的std::fstream类作为示例。
概念是如果析构函数调用任何可能引发异常的方法,则这些方法应该是公共的。因此,如果您对象的用户想要检查异常,他们可以使用公共方法并处理异常。如果他们不关心异常,那就让析构函数处理问题。
回到std::fstream的示例。
{
std::fstream text("Plop");
// Load Text.
// I don't care if the close fails.
// So let the destructor handle it and discard exceptions
}
{
// If this fails to write I should at least warn the user.
// So in this case I will explicitly try and close it.
try
{
std::ofstram password("/etc/password");
// Update the password file.
password.close();
}
catch(...)
{
Message.ShowDialog("You failed to update the Password File");
}
}
简单的回答是,永远不要在析构函数中抛出异常!
复杂的回答是,只有当另一个异常处于活动状态时,异常从析构函数中逃逸才会真正让你陷入困境。这种情况通常发生在你已经从另一个异常中解开堆栈并销毁了相关对象的情况下。在这种情况下,如果异常从析构函数中逃逸,则会调用std::terminate
,请注意,您可以通过调用std::set_terminate
来设置自己的std::terminate
处理程序。默认实现std::terminate
是调用abort。
更为复杂的是,大多数希望对其异常安全性做出任何保证的函数,主要是基本保证或强保证,都依赖于底层类型本身在其析构函数中不抛出异常*
真正的问题是,当此错误发生时,您的程序将处于什么状态?您如何恢复?应该在哪里处理此恢复?您需要查看特定情况并解决这些问题。有时捕获异常并忽略它就足够了。其他时候,您需要引起一些警报。
因此,答案是:C++允许在析构函数中抛出异常,但您永远不应该允许它逃逸。
- 回顾:简要定义Abrahams异常安全保证(基本、强和nothrow)。
基本保证是,失败的操作可能会改变程序状态,但不会发生泄漏,并且受影响的对象/模块仍然可析构和使用,在一个一致(但不一定可预测)的状态下。
强保证涉及事务提交/回滚语义:失败的操作保证程序状态在所操作对象方面没有改变。这意味着没有影响对象的副作用,包括相关助手对象(如指向正在操作的容器中的迭代器)的有效性或内容。
nothrow保证意味着不会发生失败的操作。该操作不会抛出异常。
您可能会发现C++ FAQ Lite中的此页面信息丰富。基本答案是“不要这样做”。您计划在哪里捕获此异常?无论您打算在捕获该异常时执行什么操作,都可以通过函数调用或其他方式来完成(例如记录日志或设置标志以警告用户等)。从析构函数中抛出异常会导致整个程序终止的风险。