从析构函数中抛出异常

从析构函数中抛出异常

大多数人说绝不可能从析构函数中抛出异常-这样做会导致未定义的行为。Stroustrup指出“向量析构函数显式地为每个元素调用析构函数。这意味着如果一个元素析构函数抛出,向量销毁就会失败…没有什么好的方法来防止从析构函数抛出的异常,所以如果元素析构函数抛出,库就无法保证”(参见附录E3.2).

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

因此,我的问题是-如果从析构函数中抛出会导致未定义的行为,那么如何处理析构函数期间发生的错误?

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

显然,这类错误是罕见的,但有可能发生。


烙印99
浏览 764回答 3
3回答

繁花如伊

从析构函数中抛出异常是危险的。如果另一个异常已经在传播,应用程序将终止。#include&nbsp;<iostream>class&nbsp;Bad{ &nbsp;&nbsp;&nbsp;&nbsp;public: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Added&nbsp;the&nbsp;noexcept(false)&nbsp;so&nbsp;the&nbsp;code&nbsp;keeps&nbsp;its&nbsp;original&nbsp;meaning. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Post&nbsp;C++11&nbsp;destructors&nbsp;are&nbsp;by&nbsp;default&nbsp;`noexcept(true)`&nbsp;and &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;this&nbsp;will&nbsp;(by&nbsp;default)&nbsp;call&nbsp;terminate&nbsp;if&nbsp;an&nbsp;exception&nbsp;is &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;escapes&nbsp;the&nbsp;destructor. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;But&nbsp;this&nbsp;example&nbsp;is&nbsp;designed&nbsp;to&nbsp;show&nbsp;that&nbsp;terminate&nbsp;is&nbsp;called &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;if&nbsp;two&nbsp;exceptions&nbsp;are&nbsp;propagating&nbsp;at&nbsp;the&nbsp;same&nbsp;time. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;~Bad()&nbsp;noexcept(false) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;1; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}};class&nbsp;Bad2{ &nbsp;&nbsp;&nbsp;&nbsp;public: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;~Bad2() &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;1; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}};int&nbsp;main(int&nbsp;argc,&nbsp;char*&nbsp;argv[]){ &nbsp;&nbsp;&nbsp;&nbsp;try &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Bad&nbsp;&nbsp;&nbsp;bad; &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;catch(...) &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;<<&nbsp;"Print&nbsp;This\n"; &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;try &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(argc&nbsp;>&nbsp;3) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Bad&nbsp;&nbsp;&nbsp;bad;&nbsp;//&nbsp;This&nbsp;destructor&nbsp;will&nbsp;throw&nbsp;an&nbsp;exception&nbsp;that&nbsp;escapes&nbsp;(see&nbsp;above) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw&nbsp;2;&nbsp;&nbsp;&nbsp;//&nbsp;But&nbsp;having&nbsp;two&nbsp;exceptions&nbsp;propagating&nbsp;at&nbsp;the &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;same&nbsp;time&nbsp;causes&nbsp;terminate&nbsp;to&nbsp;be&nbsp;called. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Bad2&nbsp;&nbsp;bad;&nbsp;//&nbsp;The&nbsp;exception&nbsp;in&nbsp;this&nbsp;destructor&nbsp;will &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;cause&nbsp;terminate&nbsp;to&nbsp;be&nbsp;called. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;catch(...) &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;std::cout&nbsp;<<&nbsp;"Never&nbsp;print&nbsp;this\n"; &nbsp;&nbsp;&nbsp;&nbsp;}}这基本上可以归结为:任何危险的事情(即可能抛出异常)都应该通过公共方法(不一定是直接的)来完成。然后,类的用户可以通过使用公共方法和捕捉任何潜在的异常来处理这些情况。然后,析构函数将通过调用这些方法来结束对象(如果用户没有显式地调用这些方法),但是任何异常抛出都会被捕获并删除(在试图修复问题之后)。因此,实际上,您将责任传递给用户。如果用户能够纠正异常,他们将手动调用适当的函数并处理任何错误。如果对象的用户不担心(因为对象将被销毁),那么析构函数将留给处理业务。例如:STD:fstreamClose()方法可能引发异常。如果已打开文件,则析构函数将调用CLOSE(),但确保任何异常都不会传播到析构函数之外。因此,如果文件对象的用户希望对与关闭文件相关的问题进行特殊处理,他们将手动调用Close()并处理任何异常。另一方面,如果他们不关心,那么破坏者将留下处理情况。ScottMyers在他的书“有效C+”中有一篇关于这个主题的优秀文章。编辑:显然也是在“更有效的C+”中项目11:防止异常离开析构函数

梦里花落0921

抛出析构函数会导致崩溃,因为这个析构函数可能被称为“堆栈展开”的一部分。堆栈展开是在抛出异常时发生的过程。在这个过程中,从“try”到异常抛出的所有被推到堆栈中的对象都将被终止->它们的析构函数将被调用。在此过程中,另一个异常抛出是不允许的,因为不可能一次处理两个异常,因此,这将引发对ABORT()的调用,程序将崩溃,控件将返回到操作系统。
打开App,查看更多内容
随时随地看视频慕课网APP