引发异常的析构函数

Destructor throwing an exception

本文关键字:析构函数 异常      更新时间:2023-10-16

我正在阅读Scott Meyerses C++,现在我在析构函数抛出异常部分。他是这么说的:

当载体v被销毁时,它负责销毁所有 它包含的Widget。假设v有十个Widget,并且在 销毁第一个,抛出异常。其他九个 Widget仍然必须销毁(否则它们持有的任何资源将被泄漏),因此 v 应该调用它们的析构函数但假设在这些调用期间,第二个 Widget 析构函数抛出例外。现在有两个同时处于活动状态的异常

因此,据

我所知,如果向量的第一个元素抛出异常,这并不意味着程序在此之后立即终止。该实现尝试销毁矢量中的其他对象。让我举个例子:

#include<iostream>
#include<vector>
struct A
{
    ~A(){ std::cout << "destruction" << std::endl; throw std::exception(); }
};
int main()
{
    A a[] = {A(), A(), A(), A(), A(), A()};
    std::vector<A> v;
    v.assign(a, a+6);
}

演示

程序在第一次引发异常后立即终止。承诺的第二个例外在哪里抛出?

如果标准库组件调用的析构函数通过异常退出,则行为未定义。[res.on.functions]/p2,强调我的:

具体而言,在以下情况下未定义效果:

  • [...]
  • 如果任何替换函数或处理程序函数或析构函数操作通过异常退出,除非 适用的必需行为:段落。
  • [...]

您指的是第 8 项"防止异常离开析构函数"。您对文本的引用将其截断在句子中间:

现在有两个同时处于活动状态的异常,这对C++来说太多了。根据出现此类同时活动异常对的精确条件,程序执行要么终止,要么产生未定义的行为。

表明你"取消了它,如果向量的第一个元素抛出异常,这并不意味着程序在那之后立即终止"

好吧,如果你没有抓住它,它就会这样做,因为这就是未捕获的异常所做的。

或者,你抓住它。通过这样做,你已经离开了进行破坏的执行框架,所以无论发生什么,都不再发生。

// speculative "what if"
struct A { ~A() { raise std::exception(); } };
void f() {
   A a, b;
} // b and a go out of scope, the first one throws
int main() {
    try {
        f();
    } catch (...) {
        // << program is now here.
    }
}

没有机制可以"恢复"f()的终止代码,发生异常,该代码路径已完成,因此如果a破坏,b永远不会破坏;反之亦然。

好的,你说的是向量。所以你认为,std::vector::clear正在做这样的事情:

clear() {
    while (!empty()) {
        try {
            erase(back());
        } catch (std::exception& e) {
            // something went wrong, lets ignore it
        }
        m_size--;
    }
}

您认为"如果发生异常,实现会尝试销毁向量中的其他对象"的理解是错误的。正如 Meyers 所说,STL 不期望或鼓励析构函数的异常,因此它不会尝试处理它们。我不知道你从哪里得到这种理解,但它是不准确的,对不起,如果语言试图实现它,我们将陷入真正的混乱,因为如果出现问题到足以在尝试破坏对象时生成异常,事情会变得非常混乱。