我有以下代码:
#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实际上将其所有数据存储在堆上;这就是为什么它是可调整大小的。您可以将堆栈上的数据视为指向堆上内存(对您隐藏)的指针。