为什么C ++ 11将析构函数标记为nothrow,是否可以覆盖它?

时间:2016-11-06 23:27:23

标签: c++

到目前为止,我从未发过C ++编译器发出的单一警告,但现在VS 2015编译器似乎突然开始抱怨这个。

似乎C ++ 11隐式地将每个析构函数标记为nothrow https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(C4297)&rd=true

为什么?这可能是一个坏主意,但我想明白为什么?如果它本身并不坏,有没有办法覆盖它,以便析构函数不是一个不值得的?

P.S。我知道异常可能是邪恶的,但有时它们很有用,特别是在我的应用程序用户生成崩溃报告时。

4 个答案:

答案 0 :(得分:4)

退出范围时会调用析构函数。抛出异常时,堆栈被展开,导致退出作用域并调用析构函数。当另一个正在进行时抛出异常时,该进程将无条件地中止std::terminate。这就是为什么从析构函数中抛出是一个坏主意以及为什么析构函数被隐式标记为noexcept(== noexcept(true))*。

如果你想从析构函数中抛出,你可以明确标记它noexcept(false)以覆盖默认值。

*如果您从标记为noexcept的函数中抛出,则会立即调用std::terminate

答案 1 :(得分:2)

其他答案在解释为什么投入析构函数是一个非常糟糕的主意方面做得非常好。 Sutter& Alexandrescu在 C ++编码标准中解释(2005)" Destructors,deallocation和swap永远不会失败" ,这是另一种说法没有先决条件的方式。 Scott Meyers在 Effective C ++(3rd Ed。)(2005)"防止异常离开析构函数" Effective Modern中提供了规范建议C ++ (2014)"声明函数noexcept如果他们不会发出例外"

工程通常是权衡利弊,妥协和困难决策。有时您可能需要抛出析构函数。你最好有一个很好的理由,而且你可能错了。在这种情况下,您可以声明析构函数noexcept(false)以覆盖默认值。然后你得到C ++ 98的行为。如果你想有条件地抛出如果堆栈当前没有被另一个异常解开,你可以使用std::uncaught_exception。处理错误时发生的错误是一个兔子洞,你不应该冒险尝试。

如果您的目的是在检测到异常状态时捕获程序状态的快照,例如通过Google Breakpad打电话回家,那么异常就不是一个好的解决方案。自动堆栈展开将丢弃有关程序状态的有用信息。您通常希望在崩溃报告中保留堆栈状态,这通常意味着调用std::terminatestd::abort

答案 2 :(得分:1)

  

为什么?这可能是一个坏主意,但我想明白为什么?   如果它本身并不坏,那么有没有办法覆盖它   析构函数不是一个不值得的人吗?

在析构函数中抛出异常是一个坏主意。原因是在异常堆栈展开期间发生,即调用堆栈上所有对象的析构函数。现在,当堆栈展开作为异常的一部分发生时,析构函数再次抛出异常。您最终可能会有多个异常,然后没有处理这些多个异常的不明确方法。例如。如果有两个例外并且我有一个例外的catch块而不是其他例子,我的进程是否会终止,或者异常被捕获并进一步处理?

答案 3 :(得分:0)

因为如果在应用程序处理之前抛出异常,则当前异常程序将终止。

可能我会得到很多-1但是无论如何我会说。您可以在析构函数中抛出异常。你还可以做更多人会说它的更多事情"邪恶"一切都会好起来,没有"邪恶"部分。但你需要确切地知道你在做什么。