8 More Effective C++—条款11(析构函数内阻止异常流出)

1 提出问题

1 析构函数调用时机

析构函数会在下面两种情况下被调用:

1, 离开对象所在作用域,对象生命周期终结,析构函数被调用,对象被销毁。
2, 异常抛出引起了栈展开(stack-unwinding),离开对象的所在的作用域,对象生命周期中介,析构函数被调用。

第一种情况如下面代码所示,离开函数myfunction()后,对象myobject会自动被析构。

void myfunction() {
	MyClass myobject;
}

2 栈展开

异常被抛出后,当前函数停止执行,并查找对应的catch字句。首先检查throw是否在try内,若在,则将该异常与try对应的catch子句进行匹配。若匹配失败,则释放当前函数内存,销毁局部对象,将异常抛向上层调用函数,直到找到可以匹配的catch子句。

上面的过程就是”栈展开“。当处理catch结束后,紧接着catch之后继续执行。但是,需要注意如下几点:

1,栈展开过程只能销毁局部对象,即调用局部对象的析构函数。
若使用new在heap声明了对象,且在delete调用前出现异常,由于提前退出,在heap中声明的资源将不会被释放,造成内存泄漏。
2,析构函数应该从不抛出异常
在进行栈展开过程中,析构函数若再次抛另一个异常,则会导致标准库函数terminate被调用。terminate函数调用abort函数,程序异常退出。
3,异常与构造函数
同上篇所述,构造函数调用过程中出现异常,应保证已经被构造的成员能被恰当销毁。
4,未捕获的异常将终止程序
同上面所述,库函数terminate被调用,终止程序运行。

3 问题

正如上面所提到,若析构函数抛出异常,有两种危害:

1,异常点之后的语句无法完成,析构工作没有完成。
2,有可能是栈展开调用析构函数,两次异常抛出导致程序终结。

2 解决问题

因此,最简单粗暴的方式,就是直接拦截析构函数中抛出的异常,保证不会有更多的异常向上传递。如下面代码所示。

~Destructor() {
	try {
		doSomething();
	} catch (...) {
		// doNothing, avoid more exception
	}
}

猜你喜欢

转载自blog.csdn.net/zhizifengxiang/article/details/83212419
今日推荐