检测析构函数中的活动异常

LiK*_*Kao 8 c++ destructor exception raii

我有一个类使用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;
   }
}
Run Code Online (Sandbox Code Playgroud)

但是我不确定这是否会起作用,因为异常在调用析构函数之前已经激活,并且不是来自try块.另外我throw;在析构函数中使用了一个并且在这一点上抛出异常是一个非常糟糕的主意.所以我不会这样做,除非标准明确保证这个案例是这个规则的例外(没有双关语)(我不知道).

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

Soa*_*Box 11

std::uncaught_exception()如果抛出异常但catch尚未处理异常,则可以使用返回true .您可以在析构函数中使用此检查来决定应该或不应该执行的操作.

需要注意的是,良好的编程指南通常要求在不同情况下让析构函数表现出不同的表现通常不是一个好主意.所以使用此功能,但不要滥用它.一个常见的用法是让析构函数只在没有活动的未捕获异常时抛出(此函数返回false).但这种行为通常不是好的设计.如果条件足够严重以保证例外,则可能不应忽略它.并且析构函数不应该抛出异常.

例:

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

  • 这个例子是不安全的,operator <<可以抛出ios_base ::失败,你需要额外的try/catch(...)在日志周围 (6认同)
  • @Deduplicator 这个问题似乎已经被标准委员会承认了。请参阅下面的答案,它表明“std::uncaught_exception()”已被弃用并被改进版本取代。 (2认同)

Hol*_*Cat 4

没有办法bool std::uncaught_exception()正确使用,所以在C++20中被删除了。我们现在有int std::uncaught_exceptions().

您需要调用它两次:事先一次(例如在构造函数中),然后在析构函数中再次调用。如果第二次调用返回一个更大的数字,则意味着析构函数因异常而被调用。否则它将返回相同的数字。

struct A
{
    int e = std::uncaught_exceptions();

    ~A()
    {
        if (std::uncaught_exceptions() > e)
            // An exception was thrown.
    }
};
Run Code Online (Sandbox Code Playgroud)