为什么在 main 中未捕获异常时不调用析构函数?

Why aren't destructors called when exception isn't caught within main?

本文关键字:调用 析构函数 捕获异常 main 为什么      更新时间:2023-10-16

我有以下代码:

#include <iostream>
#include <vector>
#include <tr1/memory>
struct FooError {};
struct Foo
{
    ~Foo() { std::cerr << "~Foo() executed" << std::endl; }
    explicit Foo(unsigned int index) { if (5 == index) throw FooError(index); };
};

int main() {
    typedef std::tr1::shared_ptr<Foo> FooPtr;
    std::vector<FooPtr> foos;
    for (unsigned int index = 0; index < 20; ++index)
    {
        try
        {
            foos.push_back(FooPtr(new Foo(index)));
        }
        catch (const FooError&)
        {
            std::cerr << "FooError caught" << std::endl;
        }
    }
}

当有try{} catch{}块时,我看到一组~Foo()被执行。当没有异常处理程序时,不会打印任何内容。这是否意味着在处理异常时调用堆栈分配对象的析构函数?或者由于std::cerr缓冲问题而没有打印任何东西?

以下是c++ 03标准中发生的细节。

  • 出自15.3/9处理异常

    如果程序中没有找到匹配的处理程序,则调用terminate()函数;

  • 出自18.6.3异常终止:

    实现的默认terminate_handler调用abort()。

  • And from 3.6.3/4 Termination:

    调用<cstdlib>中声明的函数void abort();将终止程序,而不执行自动或静态存储时间的对象的析构函数,也不调用传递给atexit()的函数。

所以这就是为什么你的foos对象没有被破坏(它有静态存储持续时间)。然而,即使您将其更改为局部变量(具有自动持续时间),也可能无法解决问题(强调):

因此,对于static duration对象,除非您更改终止处理程序(可能让它调用exit()而不是abort()),否则不会调用析构函数。然而,对于自动对象,仍然存在一个可能的问题(强调):

15.5.1/1 terminate()函数

如果没有找到匹配的处理程序,则为实现定义的,无论堆栈之前是否展开调用Terminate()。在所有其他情况下,堆栈不应在terminate()被调用之前展开。

程序的范围展开,无论是通过正常执行还是通过try/throw/catch,只有当应用程序从main返回退出时才会发生。如果应用程序通过异常(或通过abort()terminate())退出,则不会进行unwind,也不会调用析构函数。

这适用于自动对象和静态对象。

析构函数在循环之后,程序退出之前被调用(从vector中)。

如果没有捕捉到异常,则调用terminate,终止程序而不调用析构函数。

如果捕捉到异常,将调用释放分配器来清理内存。如果你没有捕捉到异常,应用程序就会退出。

顺便说一下,vector实际上将其所有数据存储在堆上;这就是为什么它是可调整大小的。您可以将堆栈上的数据视为指向堆上内存(对您隐藏)的指针。