27

是否可以让析构函数捕获异常然后重新抛出它们?
如果是这样,我该怎么做,因为没有明确的try声明位置?

基本上,我想理想地做:

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

背景
我有一个大型、复杂的应用程序,它在某处抛出未处理的异常。我无法轻松访问main顶级消息泵或类似的东西,因此没有容易的地方来捕获所有未处理的异常。

我认为任何未处理的异常都必须通过一堆析构函数,因为堆栈是展开的。所以,我正在考虑在析构函数中分散一堆catch语句。然后至少我会知道抛出异常时有哪些对象在起作用。但我不知道这是否可行或可取。

4

3 回答 3

17

编辑:您可以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. 如果您希望在未捕获异常并即将终止程序时调用方法,这将很有用。在终止处理程序中,我通常会在最终终止程序之前打印一些关于异常的信息和它来自哪里的(解构的)回溯到我的日志文件。这是编译器和系统特定的,但它是一个真正的帮手,因为如果您编写服务器进程,它可以节省大量时间,而且通常日志文件就是您从 ops 获得的全部。

于 2013-09-23T15:54:15.130 回答
8

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

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

X::~X() {
    if (std::uncaught_exception()) {
        try {
            LogSomeInfo();
            // and do something else...
        } catch(...) {}
    }
}
于 2013-09-23T18:35:15.527 回答
7

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

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

处理异常非常困难,比人们乍一看可能想的要困难得多,原因是异常安全不会因组成而扩展。在我看来,这意味着基本上不可能拥有具有强大异常安全性的非平凡有状态子系统(如果抛出异常,内部状态不会发生任何事情)。这是很久以前发现的(参见 1994 年 Tom Cargill 的“异常处理:一种虚假的安全感”),但显然仍然被大部分 C++ 社区所忽视。

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

在所有其他情况下,捕获异常时系统的全局状态充其量在捕获时是不确定的,在我看来,在这种情况下,除了尽可能大声地立即死亡之外,您可以在这种情况下做任何事情的用例很少而不是在不知道发生了什么的情况下采取进一步的行动(死程序不会说谎)。在我看来,即使继续调用析构函数也有些问题。

或者您可能会尝试尽可能地发挥功能,但这也不是一条容易的道路(至少对我的大脑而言)而且它也远离现实(大多数计算机是具有数十亿位可变状态的可变对象:您可以假装情况并非如此,而是它们是没有状态的数学函数,并且具有依赖于输入的可预测输出……但在我看来,您只是在自欺欺人)。

于 2013-09-23T16:40:37.777 回答