【发布时间】:2021-05-27 03:52:41
【问题描述】:
我在 C++ 入门第 5 版第 18 章的异常处理中读到,析构函数通常不应该像 STL 容器那样抛出异常,如果应该抛出的表达式应该包装在 try-catch 块中(catch 必须处理那个例外)。
-
为了理解目的,我已经尝试过这个:
struct Foo{ Foo(){ std::cout << "Foo()\n"; } ~Foo(){ std::cout << "~Foo()\n"; throw "exception thrown from Foo::~Foo()\n"; std::cout << "Back in Foo::~Foo()\n"; } }; int main(){ try{ Foo f{}; Foo* pFoo = new Foo{}; delete pFoo; // normally exception caught here nad handled by the following catch } catch(char const* const& cp){ std::cout << cp << '\n'; } std::cout << '\n'; } -
恕我直言
Foo的析构函数不应该在这里抛出该异常,因为析构函数之外的处理程序永远无法捕获该异常;因为每当对象超出范围并且try-catch块总是在块内时都会调用析构函数,换句话说,析构函数是在try-catch blcok之后调用的。 -
但正如我们所知,我们可以过早地调用析构函数,例如显式调用它或删除指向有效动态内存的类/结构类型的指针。所以在这种情况下,我们可以在
try-block中调用该析构函数,通常可以由相应的处理程序处理。 -
但是我的程序总是调用
terminate()(作为未处理异常的标志)并且我收到警告:
warning: throw will always call 'terminate' [-wterminate].
-
如您所见,我在
try-block中调用Foo析构函数main:delete Foo。如果我写的话,我也可以这样做:f.~Foo();。 -
那么有人能解释一下这背后的原因吗?谢谢!
【问题讨论】:
-
我之前和现在删除的评论是一个错误。您将 try/catch 放置在错误的位置。析构函数一般不应该抛出。如果必须,则必须在析构函数本身内处理。例外是针对特殊情况。特殊情况往往会排除析构函数,因为析构函数会完成它们的工作。
-
由于 C++11 的析构函数中的异常是被禁止的,除非析构函数被显式声明为
noexcept(false)。析构函数被隐式声明为noexcept(true)。 -
仅供参考:C++ FAQ: How can I handle a destructor that fails?:将消息写入日志文件。终止进程。或者打电话给蒂尔达阿姨。但不要抛出异常!
-
至于析构函数中的异常不好的原因,假设其他一些代码抛出了异常。在从该初始异常中展开调用堆栈时,本地对象的破坏会引发第二个嵌套异常,您无法捕获。
-
旁注:g++ 6 或更高版本给出了一个很好的提示:注意:在 C++11 中,析构函数默认为 'noexcept'。用 Godbolt 戳了一下,看起来 clang 5+ 给出了类似的注释。