【问题标题】:Detecting active exceptions in the destructor检测析构函数中的活动异常
【发布时间】:2011-07-27 13:30:05
【问题描述】:

我有一个类使用 RAII 进行清理,以防出现问题。这意味着该类包含一个标志,告诉它工作是否已完成,如果在调用构造函数时未设置此标志,则它正在执行它的清理任务并生成日志消息。现在我希望这个类变得更加聪明,即它应该找出错误是否发生,因为工作被中止(即抛出异常并且调用了析构函数)或者因为有人滥用这个类并且永远不会实际上完成了工作。这意味着我必须在析构函数中找出异常是否处于活动状态。如果找到一个,我会生成一条日志消息,可能会打印异常的内容,然后重新抛出它。我猜是这样的。

Foo::~Foo () {
  try { /* do not know what to put here */ }
  catch( const std::exception & ex ) {
     // produce error in log
     throw;
  }
  catch( ... ) {
    // produce some other log message
    throw;
   }
}

但是我不确定这是否会起作用,因为异常在调用析构函数之前就已经处于活动状态并且不是源自try 块。另外我在析构函数中使用了throw;,此时抛出异常是一个非常糟糕的主意。所以我不会这样做,除非标准明确保证这种情况是这个规则的例外(不是双关语)(我不知道)。

那么这是否可能,或者我应该以其他方式处理这种情况?

【问题讨论】:

    标签: c++ exception destructor raii


    【解决方案1】:

    您可以使用std::uncaught_exception(),如果抛出异常但catch 尚未处理它,则返回true。您可以在析构函数中使用此检查来决定它应该做什么或不应该做什么。

    请注意,良好的编程指南通常规定,让您的析构函数在不同情况下的行为明显不同通常不是一个好主意。所以使用这个功能,但不要滥用它。一个常见的用途是有一个析构函数,只有在没有活动的未捕获异常时才抛出(此函数返回 false)。但这种行为通常不是好的设计。如果条件差到足以保证例外,它可能不应该被忽略。而且析构函数无论如何都不应该抛出异常。

    例子:

    Foo::~Foo()
    {
        if (std::uncaught_exception()) 
        {
            std::cerr << "Warning: Can't cleanup properly" << std::endl;
            return;
        }
        else
        {
            // ...
        }
    }
    

    【讨论】:

    • 这个例子是不安全的,operator
    • 那简直坏掉了。如果在构造Foo的时候已经出现异常怎么办?
    【解决方案2】:

    不可能以安全的方式进行。

    您可以做的是检查析构函数中的参数,这应该可以判断处理是否完成。

    顺便说一句,你为什么不在你可以实际处理错误的 catch 块中记录错误?在那里您应该知道处理已因错误而终止。

    【讨论】:

    • 是的,我已经在检查参数了。但是,我的班级正在做的事情是您要执行多个操作,然后执行原子提交。但是我想检查一下,如果真的出了问题,其他代码永远不会进入提交步骤。另一方面,现在的日志不会表明是否发生了不好的事情,是否没有执行提交,或者程序员是否只是忘记了编写提交。这第二种情况是我想抓住的。
    【解决方案3】:

    看看这个question of mine and the responses

    基本上,您可以在第一个 try 块中使用简单的throw 重新引发当前活动的异常。但是,只有在您确定当前正在处理异常时才安全,即从 catch 块中调用析构函数。否则,throw 将调用 @987654325 @。

    Foo::~Foo () {
      try {  throw; } // only if you *know* that an exception is active - bad thing for a generic destructor
      catch( const std::exception & ex ) {
         // produce error in log
    
      }
      catch( ... ) {
        // produce some other log message
    
       }
    }
    

    但是,从析构函数中抛出异常是不受欢迎的,所以我不会在之后重新抛出异常。总而言之,整件事在我看来并不是一个好主意。

    异常处理模式很好,但我不会在不相关的析构函数中这样做。再次,请参阅引用的问题。

    【讨论】:

      猜你喜欢
      • 2011-12-19
      • 1970-01-01
      • 2012-11-27
      • 2017-08-16
      • 2014-01-21
      • 2012-04-11
      • 1970-01-01
      • 2011-10-31
      • 2013-08-13
      相关资源
      最近更新 更多