大多数人说 永远不要 从析构函数中抛出异常——这样做会导致未定义的行为。Stroustrup 指出 “向量析构函数显式调用每个元素的析构函数。这意味着如果元素析构函数抛出,向量析构失败......确实没有很好的方法来防止析构函数引发的异常,所以库不保证元素析构函数是否抛出”(来自附录 E3.2) 。
这篇文章似乎另有说法 - 抛出析构函数或多或少是可以的。
所以我的问题是——如果从析构函数中抛出导致未定义的行为,你如何处理析构函数期间发生的错误?
如果在清理操作过程中发生错误,您是否忽略它?如果它是一个可以在堆栈中处理但不能在析构函数中正确处理的错误,那么从析构函数中抛出异常是否有意义?
显然,这类错误很少见,但也有可能。
从析构函数中抛出异常是危险的。 如果另一个异常已经在传播,则应用程序将终止。
#include <iostream> class Bad { public: // Added the noexcept(false) so the code keeps its original meaning. // Post C++11 destructors are by default `noexcept(true)` and // this will (by default) call terminate if an exception is // escapes the destructor. // // But this example is designed to show that terminate is called // if two exceptions are propagating at the same time. ~Bad() noexcept(false) { throw 1; } }; class Bad2 { public: ~Bad2() { throw 1; } }; int main(int argc, char* argv[]) { try { Bad bad; } catch(...) { std::cout << "Print This\n"; } try { if (argc > 3) { Bad bad; // This destructor will throw an exception that escapes (see above) throw 2; // But having two exceptions propagating at the // same time causes terminate to be called. } else { Bad2 bad; // The exception in this destructor will // cause terminate to be called. } } catch(...) { std::cout << "Never print this\n"; } }
这基本上归结为:
任何危险的事情(即可能引发异常)都应该通过公共方法(不一定直接)来完成。然后,您的类的用户可以通过使用公共方法并捕获任何潜在的异常来潜在地处理这些情况。
然后,析构函数将通过调用这些方法来结束对象(如果用户没有明确地这样做),但是任何抛出的异常都会被捕获并丢弃(在尝试修复问题之后)。
因此,实际上您将责任转嫁给了用户。如果用户能够纠正异常,他们将手动调用适当的函数并处理任何错误。如果对象的用户不担心(因为对象将被销毁),那么析构函数将负责处理业务。
标准::fstream
close() 方法可能会引发异常。如果文件已打开,则析构函数调用 close(),但要确保任何异常都不会传播到析构函数之外。
因此,如果文件对象的用户想要对与关闭文件相关的问题进行特殊处理,他们将手动调用 close() 并处理任何异常。另一方面,如果他们不关心,那么析构函数将被留下来处理这种情况。
Scott Myers 在他的“Effective C++”一书中有一篇关于这个主题的优秀文章
显然也在“更有效的 C++” 第 11 条:防止异常离开析构函数