C++ 在析构函数和后续析构函数中引发异常

C++ 在析构函数和后续析构函数中引发异常,c++,c++17,C++,C++17,我做了一个从析构函数抛出异常的实验,得到了一个我没有预料到的结果。请参阅代码片段: #include <iostream> #include <exception> class A { public: ~A() noexcept(false) { std::cout << "in ~A" << std::endl; //throw std::runtime_error(&quo

我做了一个从析构函数抛出异常的实验,得到了一个我没有预料到的结果。请参阅代码片段:

#include <iostream>
#include <exception>

class A
{
public:

    ~A() noexcept(false)
    {
        std::cout << "in ~A" << std::endl;
        //throw std::runtime_error("~A exception");
    }
};

class M
{
public:

    ~M() noexcept(false)
    {
        std::cout << "in ~M" << std::endl;
        //throw std::runtime_error("~M exception");
    }
};

class B : public A
{
public:

    ~B() noexcept(false)
    {
        std::cout << "in ~B" << std::endl;
        throw std::runtime_error("~B exception");
    }
    
private:

    M m;
};

class X
{
public:

    ~X() noexcept(false)
    {
        std::cout << "in ~X" << std::endl;
        //throw std::runtime_error("~X exception");
    }
};

int main()
{
    try
    {
        X x;
        B b;
    }
    catch(const std::exception& e)
    {
        std::cerr << e.what() << '\n';
    }
    
    return 0;
}
所以问题是:如果我从析构函数抛出一个异常,它被捕获,那么所有后续的析构函数(包括基类的析构函数、类成员和堆栈上声明的对象的析构函数(例如X))都被调用,然后异常被重新调用,这是正确的吗

EDIT1:

如果我修改
main()
如下,结果相同:

int main()
{
    try
    {
        X x;
        B * b = new B();
        delete b;
    }
    catch(const std::exception& e)
    {
        std::cerr << e.what() << '\n';
    }
    
    return 0;
}
intmain()
{
尝试
{
X;
B*B=新的B();
删除b;
}
捕获(const std::exception&e)
{
标准:cerr
然后例外情况是重新收回

不,它不是“rethrown”。在抛出(和捕获)异常的过程中,调用析构函数。这在语义上稍有不同,但异常只抛出一次,在这里,对象作为抛出异常的一部分被销毁

让我们抛开从析构函数中抛出异常的问题(这本身就是一个不合理的话题)。请考虑下面的代码:

void function()
{
    X some_object;

    throw std::runtime_error("bad");
}
X
的析构函数在这里被调用,作为抛出异常的一部分。为什么?因为对象正在被销毁。析构函数的调用与此函数正常返回时调用的析构函数没有什么不同。唯一的区别是,执行不会返回调用方,而是返回到exc所在的位置eption被捕获(假定它被捕获)。但是,无论哪种方式,执行都会离开这个作用域,这意味着自动确定作用域的对象必须被销毁,这意味着它的析构函数必须被调用

每当执行离开对象的自动作用域时,就会调用自动作用域对象的析构函数。无论是自愿离开作用域,还是由于抛出异常而非自愿离开作用域,都没有区别


在您的情况下,这没有什么不同。事实上,您的代码已经进入了最派生对象的析构函数,作为正常销毁的一部分(通过
delete
或离开自动对象的作用域)。引发异常的事实并不会真正改变对象被销毁的事实。唯一的区别是,对象作为引发异常的一部分而被销毁,因为执行会移动到捕获它的位置,但无论执行移动到返回的位置还是捕获它的异常的位置,都会产生错误这与析构函数调用没有区别。

不是答案,但包含了关于该主题的较长讨论。这回答了你的问题吗?当然,这是一个合理的问题,但以防万一,我会在这里提出这个cpp核心C.36:析构函数不能失败。我想(我可能错了)在抛出之后,异常机制定位捕获,然后将堆栈(调用析构函数)展开到该捕获,然后异常被捕获。这不是问题的一部分,但是…如果在展开过程中,另一个析构函数也抛出异常(因此将有两个处于飞行状态,这将不起作用)异常机制终止应用程序。如果未捕获异常(“捕获并退出!”-Willy Wonka),我根本看不到正在调用析构函数。我只是看到了
std::terminate
。但这可能是一个实现细节。@Eljay--当抛出异常但未捕获时,它的实现定义了堆栈在调用
std:terminate
之前是否展开。
void function()
{
    X some_object;

    throw std::runtime_error("bad");
}