2

我有以下代码:

#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;
        }
    }
}

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

4

4 回答 4

5

以下是 C++03 标准的详细信息。

  • 从 15.3/9 开始处理异常

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

  • 从 18.6.3 异常终止:

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

  • 从 3.6.3/4 终止:

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

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

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

15.5.1/1terminate()功能

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

于 2011-12-18T19:04:37.663 回答
3

程序的作用域展开,无论是通过正常执行还是通过 try/throw/catch,只有在应用程序从main. 如果应用程序通过异常(或通过abort()or terminate())退出,则不会发生展开,也不会调用析构函数。

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

于 2011-12-18T17:27:09.783 回答
2

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

如果您没有捕获异常,则调用 terminate 中止程序而不调用析构函数。

于 2011-12-18T17:28:39.243 回答
1

如果您捕获到异常,将调用释放器来清理内存。如果您没有捕捉到异常,应用程序将退出。

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

于 2011-12-18T17:20:56.030 回答