在析构函数中捕获异常

时间:2013-09-23 15:50:13

标签: c++ exception destructor

是否可以使析构函数捕获异常然后重新抛出它们? 如果是这样,我将如何做到这一点,因为try声明没有明确的位置?

基本上,我想做到最好:

CMyObject::~CMyObject()  
{
    catch(...)    // Catch without a try.  Possible?
    {
        LogSomeInfo();
        throw;  // re-throw the same exception
    }
    // Normal Destructor operations
}

背景
我有一个庞大的,复杂的应用程序,在某处抛出未处理的异常。 我无法轻松访问main或顶级消息泵或类似内容,因此无法轻松捕获所有未处理的异常。

我认为当堆栈被解开时,任何未处理的异常都必须通过一堆析构函数。所以,我正在考虑在析构函数中散布一堆catch语句。然后,至少我会知道抛出异常时正在播放的对象。但我不知道这是否可行,或者是否可取。

3 个答案:

答案 0 :(得分:16)

编辑:您可以使用std::uncaught_exception检查当前是否正在抛出异常(即,如果由于异常而正在进行堆栈展开)。无法捕获该异常或以其他方式从析构函数中访问它。因此,如果您的日志记录不需要访问异常本身,则可以使用

CMyObject::~CMyObject()  
{
  if(std::uncaught_exception()) {
    LogSomeInfo(); // No access to exception.
  }
  // Normal Destructor operations
}

请注意,此问题是在2013年提出的,同时std::uncaught_exception已替换为std::uncaught_exceptions(请注意最后的额外s),这会返回int。有关基本原理,请参阅http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4152.pdf,因此,如果您使用的是C ++ 17,则应该更喜欢新版本。上面的文章还解释了为什么旧的std::uncaught_exception在某些情况下无法正常工作。


另一个选项可能是std::set_terminate。如果您希望在未捕获异常并且即将终止程序时调用该方法,这将非常有用。在终止处理程序中,我通常在最终终止程序之前打印一些有关异常的信息以及它来自我的日志文件的(解码)回溯。这是编译器和系统特定的,但它是一个真正的帮助器,因为它可以节省大量时间,如果您编写服务器进程,并且通常日志文件是您从操作系统获得的。

答案 1 :(得分:8)

当且仅当正在处理异常时,您可以使用std::uncaught_exception()返回true。它自C ++ 98开始提供,并被std::current_exception取代,后者返回std::exception_ptr

但是,您必须注意不要在无人看守的上下文中抛出另一个异常,否则将捕获std::terminate。例如:

X::~X() {
    if (std::uncaught_exception()) {
        try {
            LogSomeInfo();
            // and do something else...
        } catch(...) {}
    }
}

答案 2 :(得分:6)

析构函数无法捕获导致实例销毁的异常。

您只能知道在销毁过程中是否存在任何“活动异常”(请参阅​​uncaught_exception)(或者,在C ++ 17中,有uncaught_exceptions中存在多少个“活动异常”)可能之后确实会处理异常。

处理异常非常困难,比第一眼看到的人要困难得多,原因是异常安全无法按构成进行扩展。在我看来,这意味着基本上不可能有非常琐碎的有状态子系统具有强大的异常安全性(如果异常被抛出则内部状态没有发生)。这是很久以前发现的(参见1994年汤姆·卡吉尔的“异常处理:虚假的安全感”)但显然仍然被大部分C ++社区所忽视。

处理我能想到的异常的唯一合理方法是让子系统具有明确定义的接口和厚厚的“墙”(内部没有副作用可能会逃脱),并且可以重新初始化为众所周知的状态如果出现问题需要从头开始。这不是微不足道的,但可以在合理的范围内正确地完成。

在所有其他情况下,捕获异常时系统的全局状态在捕获点最多是无限期的,并且在我看来很少有用例可以在这种情况下做任何事情,除非立即死亡尽可能大声而不是在不知道发生了什么的情况下采取进一步行动(死计划不说谎)。在我看来,即使继续使用析构函数也是有点可疑的。

或者你可能尝试尽可能地发挥作用,但这也不是一条容易的道路(至少对我的大脑而言),而且它也远离现实(大多数计算机都是具有数十亿比特可变状态的可变对象) :你可以假装情况不是这样,而且它们是数学函数,没有状态,可预测的输出依赖于输入......但在我看来,你只是在欺骗自己。)