是什么导致在取消try-block期间抛出析构函数的奇怪行为?

lui*_*dim 8 c++ destructor exception try-catch language-lawyer

当try块遇到异常时,将取消堆栈堆栈。如果在try块内部创建了对象,则调用析构函数。如果析构函数引发另一个异常,则不会捕获此异常,并且程序将终止。

因此,如果您有:

struct A {
    ~A () noexcept(false) {
        std::cout << "A::~A" << std::endl;
        throw std::runtime_error("A::~A ERROR");
    }
};
Run Code Online (Sandbox Code Playgroud)

然后您的try-catch块类似于:

try {
    A a1;
    A a2;
} catch (...) {}
Run Code Online (Sandbox Code Playgroud)

然后,当try块完成时,a2抛出异常的析构函数,捕获异常,然后抛出异常的析构函数a1并终止程序。一切正常。

但是,如果您引入另一个结构,该结构也抛出了析构函数,但从成员继承A或具有A成员实例,事情就会变得混乱。例如,如果您有:

struct B : A {
    ~B () noexcept(false) {
        std::cout << "B::~B" << std::endl;
        throw std::runtime_error("B::~B ERROR");
    }
};
Run Code Online (Sandbox Code Playgroud)

然后,如果您这样做:

try {
    B b;
    A a;
} catch (...) {}
Run Code Online (Sandbox Code Playgroud)

预期的结果应该A::~A是称为异常捕获,然后B::~B称为程序终止。但是,相反,在我尝试过的所有编译器中,除MSVC之外,输出为:

A::~A

B::~B

A::~A
Run Code Online (Sandbox Code Playgroud)

抛出的实例后终止调用 std::runtime_error

  what():  A::~A ERROR
Run Code Online (Sandbox Code Playgroud)

好像捕获了两个异常,第三个异常终止了程序。

如果定义B为:

struct B {
    ~B () noexcept(false) {
        std::cout << "B::~B" << std::endl;
        throw std::runtime_error("B::~B ERROR");
    }
    A a;
};
Run Code Online (Sandbox Code Playgroud)

我还尝试了其他一些具有更多结构的组合。

不要费心把任何东西放在catch块中,因为该程序甚至永远不会去那里。

是的,我知道理想上析构函数甚至不应该抛出异常。在阅读了一篇有关抛出析构函数的文章之后,这更多是出于好奇。

fra*_*sco 5

我认为您观察到的行为取决于实现。从std :: terminate()上的c ++参考(强调我的):

由于以下任何原因导致异常处理失败时,C ++运行时将调用std :: terminate():

1)引发而不捕获异常(在这种情况下,是否完成堆栈的展开是实现定义的

在第一种情况下,退出范围:

  • 的析构函数A被调用。
  • 异常std::runtime_error("A::~A ERROR")被抛出。
  • 这样的异常被捕获catch(...)
  • 展开堆栈时,也会B调用的析构函数。
  • 此时std::terminate()称为。但是是否实现定义

    a)程序立即终止并提供您期望的输出

    要么

    b)程序展开堆栈,从而调用基类的析构函数,A然后终止;这就是您所观察到的。

实时查看Coliru上的代码