C++ 如果发生故障的析构函数可以';不要抛出异常

C++ 如果发生故障的析构函数可以';不要抛出异常,c++,C++,我注意到你不能在析构函数中抛出异常。所以我的问题是,如果析构函数失败,我应该怎么做 另一个问题是,在什么情况下,析构函数可能会失败 非常感谢设计你的课程,让他们的导师不会因为设计而失败。如果d'tor中的某个内容可能引发异常,请在d'tor主体中捕获它并将其吞下(或者按照您喜欢的方式处理它,但不要重新抛出它)。忽略错误 例如,如果类包装某种输出,并且析构函数刷新并关闭该输出,则析构函数可能“失败”。写入数据可能会失败。然后,您可以选择终止程序,或者捕获异常,忽略错误,然后返回。通常正确的设计是忽

我注意到你不能在析构函数中抛出异常。所以我的问题是,如果析构函数失败,我应该怎么做

另一个问题是,在什么情况下,析构函数可能会失败


非常感谢

设计你的课程,让他们的导师不会因为设计而失败。如果d'tor中的某个内容可能引发异常,请在d'tor主体中捕获它并将其吞下(或者按照您喜欢的方式处理它,但不要重新抛出它)。

忽略错误

例如,如果类包装某种输出,并且析构函数刷新并关闭该输出,则析构函数可能“失败”。写入数据可能会失败。然后,您可以选择终止程序,或者捕获异常,忽略错误,然后返回。通常正确的设计是忽略它

在我的示例中,该类还应该有一个“close_and_flush”函数,如果用户想知道它是否成功,可以在对象销毁之前调用该函数。如果类的用户不关心操作是否失败,那么您也不关心,您可以安全地抑制异常

然后,用户可以编写如下代码:

{
    OutputObject OO;
    write some stuff to OO, might throw;
    do more things, might throw;
    try {
        OO.flush_and_close();
    } catch (OutputException &e) {
        log what went wrong;
        maybe rethrow;
    }
}
或者这个:

try {
    OutputObject OO;
    write some stuff to OO, might throw;
    do more things, might throw;
    OO.flush_and_close();
} catch (AnyOldException &e) {
    log what went wrong;
    maybe rethrow;
}

无论哪种方式,只有当其他东西抛出异常并且对象在堆栈展开期间被销毁时,用户才会在不显式刷新的情况下销毁对象。因此,他们已经知道自己的操作失败了,如果有必要,他们可以回滚事务或其他任何必须执行的操作以响应失败。

应该编写析构函数,使其不会失败。释放资源应该是您在析构函数本身中所做的唯一事情。对于进一步的“去初始化”过程,使用不同的方法。

我不同意析构函数应该“设计成不会失败”——当然它们也会失败。例如,在析构函数中调用fclose()可能会失败。现在的问题是怎么做?在我看来,有两种选择:

  • 别理它。这具有简单的优点,但您永远不会知道失败发生了,这可能意味着隐藏bug

  • 记录下来。这样做的问题是,无法保证写入日志也不会失败,或引发其他问题

如果这让你不再聪明,那么我也一样!基本上,没有完美的解决方案。您需要根据具体情况从上述两个选项中进行选择。决定的一种方式是“如果这是C代码(它没有析构函数)我会怎么办?”——如果你忽略C中的问题,也不要在C++中忽略它,

析构函数不能抛出! RAII就是建立在这个基础上的,所以从一个毁灭者身上扔东西会打开RAII攻击你的可能性

这并不局限于C++。您通常不希望处理失败会破坏程序的执行,无论是哪种语言或框架

但我想扔!!!! 如果您认为您的目标应该在处置资源时抛出,那么您应该手动执行:

class MyObject
{
    public :
       // etc.
       ~MyObject()
       {
          try
          {
             this->dispose() ;
          }
          catch(...) { /* log the problem, or whatever, but DON'T THROW */ }
       }

       void dispose()
       {
          if(this->isAlreadyDisposed == false)
          {
             this->isAlreadyDisposed = true ;

             // dispose the acquired resource          
          }
       }
} ;
这样,默认情况下,对象将与RAII一起正常工作。但是在您应该知道dispose失败的情况下,您可以手动调用
dispose
方法并处理潜在的故障

dispose方法的确切代码取决于您想要的结果(例如,dispose是否应该是多线程安全的,dispose失败的对象是否应该被视为“已经disposed”,等等)

难道没有其他方法来表示错误吗? 当然有,但无论如何,它们都是以全球资源为基础的

例如,您可以在控制台或文本文件中记录故障

另一个方法是设置一些全局变量,但这通常是C++中最脏的技巧。另一种方法是调用某种处理程序,但同样,在一个不知道如何处理错误的泛型处理程序中,您不能做很多事情

在一个例子中,我编写了一个构造函数,在构造时引用布尔值。比如:

class MyObject
{
    bool & isOk ;

    public :
       // etc.
       MyObject(bool & p_isOk) : isOk(p_isOk) {}

       ~MyObject()
       {
          // dispose of the ressource
          // If failure, set isOk to false ;
       }
} ;
它可以用作:

void foo()
{
   bool isOk = true ;

   // etc.

   {
      MyObject o(isOk) ;
      // etc.
   }


   if(isOk == false)
   {
      // Oops...
   }
}

但这种代码应该是例外。我记得是我想象出来的,但不记得是否使用过它(虽然我使用了一个变体作为计时器…)

+1:这可能是一个堆芯泄漏,你对此无能为力。尼尔-我的回答提到使用单独的方法进行更高级的清理……fclose()将属于这一类。这样,方法可以选择如何处理异常,而不是d'tor本身。事情当然会失败,我只是更喜欢清理方法,而不是把所有的代码都放在d'tor中。即使失败,流也将与文件解除关联,因此除了日志记录之外,其他任何有用的操作都无法完成?这会不会使“反初始化”更容易被跳过?我同意UncleBens的观点。这打败了雷伊。使用异常安全代码是一种方法,对于析构函数(除其他外),代码必须为nothrow/nofail。这是100%错误的。如何确保析构函数100%安全?