在放松试验块期间,通过投掷破坏者而导致这种怪异的行为

What causes this weird behavior with throwing destructors during unwinding of a try-block?

本文关键字:破坏者      更新时间:2023-10-16

当try-block遇到异常时,堆栈就会解开。如果在try块内创建一个对象,则称为击曲线。如果破坏者抛出另一个例外,则不会捕获此例外,并且该程序被终止。

所以如果有:

struct A {
    ~A () noexcept(false) {
        std::cout << "A::~A" << std::endl;
        throw std::runtime_error("A::~A ERROR");
    }
};

然后您的尝试键盘块类似于:

try {
    A a1;
    A a2;
} catch (...) {}

然后,当try块结束时, a2的击离器抛出了异常,然后抓住了 a1的驱动器,并且程序将终止。一切都按预期工作。

但是,如果您引入了另一个也会在灾难中抛出但从A继承或以A作为成员的实例的结构,那么事情就会令人困惑。例如,如果您有:

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

,如果您这样做:

try {
    B b;
    A a;
} catch (...) {}

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

A::~A
B::~B
A::~A

丢弃 std::runtime_error

的实例后终止调用
  what():  A::~A ERROR

好像被捕了两个例外,第三个例外终止了程序。

如果将B定义为:

,也会发生同样的情况
struct B {
    ~B () noexcept(false) {
        std::cout << "B::~B" << std::endl;
        throw std::runtime_error("B::~B ERROR");
    }
    A a;
};

我还尝试了其他一些结构的组合。

不要打扰将任何东西放在接管块中,因为该程序甚至永远不会去那里。

是的,我知道理想情况下甚至不应该抛出例外。阅读了有关投掷灾难的文章后,更多的好奇心。

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

std :: terminate((在异常处理时由C 运行时调用 由于以下任何原因失败:

1(抛出一个例外 未捕获(它是实施定义的 在这种情况下完成(

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

  • 调用了A的破坏者。
  • 例外std::runtime_error("A::~A ERROR")被抛出。
  • catch(...)捕获了这样的例外。
  • 在放开堆栈时,也称为B的破坏者。
  • 此时称为std::terminate()。但是实施定义是否

    a(该程序立即终止并给出您期望的输出

    b(程序放弃堆栈,因此称为基类A的击路仪,然后终止;这就是您看到的观察。

请参阅Coliru上的代码