2011-12-18 107 views
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缓冲问题?

回答

1

如果发现异常,将调用释放器来清理内存。如果你没有发现异常,应用程序就会退出。

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

+1

+1。我没有意识到OP在谈论没有try/catch时的情况,认为这是关于何时抛出异常的问题。 – 2011-12-18 17:22:43

+1

但是我现在删除的答案仍然很重要 - 'Foo'没有堆栈分配。 – 2011-12-18 17:23:20

+0

我想知道为什么没有堆栈解绕发生。如果有几个块级别,并且在下级错误被捕获,那么我会因为FooPtr的aint释放而导致内存泄漏? – bananov 2011-12-18 17:31:33

3

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

这属于自动和静态对象。

+0

如果我们发现在发生点和main()返回点之间的中途发生了异常,但随后重新抛出它,那么析构函数是不是只会调用到我们发现异常的地方? – jrok 2011-12-18 17:50:19

+1

“不放卷” - 一般来说它是由实现定义的,不管它是否发生,在这种情况下不是。 – 2011-12-18 19:15:50

+0

@SteveJessop:你可以查看这个例子:http://ideone.com/X7Hv8?这是否意味着它的实现定义了foo的构造函数是否会被调用?或者'foo'总会被毁灭? – jrok 2011-12-18 19:23:50

2

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

如果您没有发现异常,那么会调用terminate来中止程序而不调用析构函数。

+1

“没有调用析构函数” - 这是这个实现上发生的事情,但不保证它们不会被调用(C++ 11中的15.3/9)。 – 2011-12-18 19:15:18

5

下面是来自C++ 03标准的细节。

  • 从15.3/9处理异常

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

  • 从18.6.3异常终止:

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

  • 而从3.6.3/4终止:

    拨打<cstdlib>声明的功能void abort();结束程序,而不执行析构的自动或静态存储持续时间的对象和不调用传递给功能的atexit()。

所以这就是为什么你 foos对象不被破坏(它具有静态存储持续时间)。但是,即使你改变它,这样它的(具有自动持续时间)的局部变量,可能不解决问题(强调):

所以对于static duration对象,析构函数没有被执行,除非你改变了终止处理程序(可能会呼叫exit()而不是abort())。然而,用于自动对象,但仍可能出现的问题(强调):

15.5.1/1 terminate()功能

在情况下没有匹配的处理器被发现,它是 implementation-定义是否在 terminate()被调用之前堆栈是否展开。在所有其他情况下,在调用terminate()之前,堆栈不应被解开。

+0

我不认为“foos”有静态存储时间,我认为这个定义在'main'函数中。 – 2011-12-18 19:17:31

+0

@Steve - 我认为你是对的,对我来说是一个误读。我已经更新了答案。 – 2011-12-18 19:26:15

相关问题