如何在析构函数期间检测异常是否处于活动状态?

4

在C++中,我如何在析构函数的主体中检测堆栈是否由于抛出异常而被解缠?一旦检测到,我能否获取活动异常的引用?

我之所以问这个问题,是因为我想添加一些调试代码,以解释为什么可能会出现某种情况以及是否是由于异常引起的。


4
现在看起来是阅读《C++编程中的一些思考》系列文章之一《GotW #47: 可靠地在构造函数和析构函数中调用虚函数(Virtuality)》的绝佳时机。 - Jerry Coffin
构建可能会失败,但破坏永远不会。在对象销毁期间没有异常处理机制是没有意义的。 - Mahesh
2
@Mahesh,我认为你误解了问题。这不是关于析构函数中的异常处理,而是关于检测析构函数是否由对象周围范围内抛出的异常调用。 - Christian Rau
2
总结杰里的文章:你可以检测到异常是否被抛出,但你无法检测到任何给定对象是否因栈在响应该异常时被解除而被销毁。 - Dennis Zickefoose
4个回答

7

std::uncaught_exception可以告诉你堆栈是否正在因为抛出异常而展开,这是你所问的。

然而,它并不能告诉你你可能想知道的内容:调用它的析构函数所在的对象是在正在被展开的堆栈部分,还是在由某个其他析构函数正常销毁的堆栈部分,该堆栈部分是由于非异常退出某个作用域而被销毁的:

struct A {
    ~A();
};

struct B {
    ~B();
}

int main() {
    try {
        A a;
        throw 1;
    } catch(...) {}
}

A::~A() {
    std::uncaught_exception(); // true
    B b;
}

B::~B() {
    std::uncaught_exception(); // also true, but "b" isn't being "unwound",
      // because ~A() returned, it didn't throw.
}

与DeadMG和Xeo所说的相反,您不能获取未被捕获的异常的引用。没有操作数的throw会重新抛出“当前处理的异常”,也就是说,您正在其中的catch-handler中处理异常,或者调用了您的catch-handler的异常。它不会重新抛出未捕获的异常。

这就是我对于throw无法工作的想法,谢谢你的确认。 - WilliamKF
“_whether the stack is being unwound due to an exception being thrown_” 这时有时会有两个栈:一个正在被卸载,另一个是由于卸载而被销毁对象的析构函数使用的栈。 - curiousguy
看起来@joaobapt的答案已经解决了C++17的这个问题,因此析构函数~B不会认为它是由于异常而被销毁。 - Mark Lakata

2

C++17的新特性std::uncaught_exceptions()是什么?我认为你可以编写以下类似的代码:

class ExceptionSentinel
{
    int prev_uncaught;

public:
    ExceptionSentinel() : prev_uncaught(std::uncaught_exceptions()) {}

    ~ExceptionSentinel()
    {
        int cur_uncaught = std::uncaught_exceptions();
        if (cur_uncaught > prev_uncaught)
        {
            // ... ExceptionSentinel is being destructed by an stack unwinding process
        }
        else
        {
            // ... Normal destruction of ExceptionSentinel
        }
    }
};

在这种情况下,std::uncaught_exceptions() 跟踪代码调用时未捕获异常的数量。更多信息可以在cppreference页面上找到。

0

有一个 std::uncaught_exception() 函数。然而,你能做的唯一有用的事情是重新抛出它并尝试捕获它以获取异常对象的访问权限。通常情况下,你不应该从任何析构函数中抛出异常。


你如何重新抛出未被捕获的异常? - WilliamKF
try{ throw; } catch(std::exception const& e){ /*handle it*/} - Xeo
“有一个std::uncaught_exception()函数。”并不能回答这个问题。 - curiousguy

-1
在C++中,我如何在析构函数的主体中检测堆栈是否由于抛出异常而被解除?

不行。
一旦检测到,我能否获取活动异常的引用?
不行。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接