C++ 这是从析构函数引发异常的安全方法吗?

C++ 这是从析构函数引发异常的安全方法吗?,c++,exception,c++11,C++,Exception,C++11,我知道从析构函数抛出通常是个坏主意,但我想知道是否可以使用std::uncaught_exception()从析构函数安全抛出 考虑以下RAII类型: struct RAIIType { ... ~RAIIType() { //do stuff.. if (SomethingBadHappened()) { //Assume that if an exception is already active, we don't really

我知道从析构函数抛出通常是个坏主意,但我想知道是否可以使用
std::uncaught_exception()
从析构函数安全抛出

考虑以下RAII类型:

struct RAIIType {
   ...

   ~RAIIType() {
      //do stuff..
      if (SomethingBadHappened()) {
           //Assume that if an exception is already active, we don't really need to detect this error
           if (!std::uncaught_exception()) {
               throw std::runtime_error("Data corrupted");
           }
      }
   }
};

这是c++11中的UB吗?这是一个糟糕的设计吗?

如果您有一个
,您是否考虑过“其他”条件?它可以抛出异常或。。。做什么?在另一个分支中可以有两种情况

  • Nothing(如果错误发生时不需要发生任何事情,为什么要抛出异常?)
  • 它“处理”异常(如果可以“处理”,为什么要抛出异常?)

既然我们已经确定了这样有条件地抛出一个异常是没有意义的,那么剩下的问题就没有什么意义了。但这里有一个小贴士:永远不要从析构函数抛出异常。如果对象抛出异常,调用代码通常会以某种方式检查该对象以“处理”异常。如果该对象不再存在,通常无法“处理”异常,这意味着不应抛出异常。要么它被忽略,要么程序生成转储文件并中止。所以从析构函数抛出异常是毫无意义的,因为捕获它是毫无意义的。考虑到这一点,类假设析构函数不会抛出,并且如果析构函数抛出,几乎每个类都会泄漏资源。所以永远不要从析构函数抛出异常。

这取决于您所说的“安全”是什么意思

这将防止从析构函数引发的问题之一——如果在处理另一个异常时堆栈展开期间发生错误,程序将不会终止

但是,仍然存在一些问题,其中包括:

  • 如果你有一个数组,那么如果你投掷毁灭,它们可能不会全部被毁灭
  • 一些异常安全习惯用法依赖于非抛出销毁
  • 许多人(比如我自己)不知道如果析构函数抛出什么将被正确销毁,什么将不会被正确销毁的所有规则,也不相信他们能够安全地使用您的类

请注意,您的代码并不像您认为的那样工作。如果发生了
somethingbad
并且没有适当的堆栈展开,您尝试从析构函数中抛出,但是调用了
std::terminate
。这是C++11中的新行为(请参阅)。您需要使用
noexcept(false)
规范对析构函数进行注释

假设你这样做,不清楚你所说的“安全”是什么意思。析构函数从不直接触发
std::terminate
。但是调用
std::terminate
并不是一个UB:它定义得很好并且很有用(请参阅)

当然,您不能将类
RaiitType
放入STL容器中。C++标准显式调用UB(当析构函数抛出到STL容器中)。 此外,设计看起来很可疑:if语句的真正意思是“有时报告失败,有时不报告”。你对这个满意吗

有关类似的讨论,请参见

我知道从析构函数抛出通常是个坏主意,但我想知道是否可以使用
std::uncaught_exception()
从析构函数安全抛出

您可能想看看Herb Sutter的提案:

动机

std::uncaught_exception
已知在许多情况下“几乎有用”,例如在实现Alexandrescu样式的ScopeGuard时。[1] 特别是,当在析构函数中调用时,C++程序员通常期望的是什么,基本上是正确的:“unctTuxExtReue返回true,IFF这个堆栈展开过程中调用这个析构函数。” 然而,正如至少自1998年以来在中所记录的那样,这意味着从析构函数传递调用的代码本身可以在堆栈展开过程中调用,但无法正确地检测它本身是否实际作为展开的一部分被调用。一旦你处于任何异常的解除状态,对于未捕获的异常,所有的事情看起来都像是解除状态,即使存在不止一个异常 一个活动异常

本文提出了一个新函数int
std::uncaught_exceptions()
,它返回当前活动的异常数,即抛出或重新抛出但尚未处理的异常数

想要知道其析构函数是否正在运行以释放此对象的类型可以在其构造函数中查询未捕获的\u异常并存储结果,然后在其析构函数中再次查询未捕获的\u异常;如果结果不同,则此析构函数将作为堆栈展开的一部分被调用 由于在对象构造之后引发的新异常


一切都解释清楚了。@AndyProwl几乎解释了一切。确实如此。我没有费心写一个答案,因为它可能只是那篇文章的一个糟糕或部分副本有趣的是,我最近了解了
uncaught_exceptions
,但我忘了我发布了这个问题。
uncaught_exceptions()
不仅仅是一个提议,而是
bool std::uncaught_exceptions()<代码>部分C++标准,请参阅我前面给出的答案。@沃尔特,你似乎把事情弄糊涂了。代码>标准::未捕获的异常
将在C++17中提供,请参阅。例如,gcc-5.2没有提供它。@MaximeGroushkin Ahem。对不起,是你把事情弄糊涂了。正如您提到的资源所说,
int std::uncaught_exceptions()
将在C++17中可用,但是
bool std::uncaught_exceptions()
(如我在前面的评论中所述)在C++11中可用(并且将在C++17中被弃用,另请参阅)。该网站没有很好地说明这一点,但例外部分给出了一个提示。使用这种方法的代码在gcc 5.1中编译得很好。@Walter Dude,你总是把
uncaught_exception
uncaught_exce混淆