异常处理深度解析
问题:
如果在main函数中抛出异常会发生什么?
#include <iostream>
using namespace std;
class Test
{
public:
Test()
{
cout << "Test()" << endl;
}
~Test()
{
cout << "~Test()" << endl;
}
};
int main()
{
static Test t;
throw 1;
return 0;
}
不同的编译器打印结果是不同的,但是根据我们编写的程序,不管哪款编译器Test()是肯定会被打印的。差异就在下面打印的东西。使用的该款编译器,当抛出异常时terminate函数被调用了。
如果异常无法被处理,terminate()结束函数会被自动调用
默认情况下,terminate()调用库函数abort()终止程序
abort()函数使得程序执行异常而立即退出
C++支持替换默认的terminate()函数实现
#include <iostream>
#include <cstdlib>
#include <exception>
using namespace std;
void my_terminate()
{
cout << "void my_terminate()" << endl;
exit(1);
}
class Test
{
public:
Test()
{
cout << "Test()" << endl;
}
~Test()
{
cout << "~Test()" << endl;
}
};
int main()
{
set_terminate(my_terminate);
static Test t;
throw 1;
return 0;
}
exit函数会确保所有的全局对象和静态局部对象全部被正常的析构。如果不调用exit,而是调用abort,又会发生什么?
不会调用析构函数,abort是异常终止一个程序,当它异常终止一个程序时,不会调用任何对象的析构函数。
通过本实验,可以得出下面两条结论:
1.在main函数中所扔出的异常如果没有被处理,最终将被一个全局函数处理掉。
2.c++编译器之间存在差异
如果析构函数中抛出异常会发生什么情况?
#include <iostream>
#include <cstdlib>
#include <exception>
using namespace std;
void my_terminate()
{
cout << "void my_terminate()" << endl;
exit(1);
}
class Test
{
public:
Test()
{
cout << "Test()" << endl;
}
~Test()
{
cout << "~Test()" << endl;
throw 2;
}
};
int main()
{
set_terminate(my_terminate);
static Test t;
throw 1;
return 0;
}
你会发现全局函数my_terminate被重复的调用。
已经调用了析构函数,又重复调用析构函数。类似于一个指针指向了堆空间中的一片内存,delete一次就把它释放掉了,如果delete两次就会有问题,造成整个应用程序的不稳定。
当把exit函数换成abort时,又会怎样呢?前面已经说过abort函数是不会调用析构函数的
从而就解释了,在terminate函数中,C++默认调用的是abort,而不是exit了。因为abort函数强制的结束当前的应用程序了,它不会去调用析构函数,就是害怕析构函数中又抛出异常
现在可以回答上面提出的问题了,可以在析构函数中扔出异常,但一定不要这样做。这样做会导致很多问题。
原则:在析构函数中一定不要抛出异常,原因如下:
析构函数是用来释放资源的地方,如果抛出异常,有可能导致资源得不到正确的释放
在析构函数里面抛出异常有可能导致全局的结束函数terminate被重复的调用,这是极其可怕的事情,有可能让系统进入极其不稳定的状态。
小结:
terminate函数 可以在调用abort函数之前,打印一些字符串,弹出一个对话框出来。