条款08 别让异常逃离析构函数:
如果在析构函数当中发生了异常,程序可能会过早结束或者导致不明确行为(异常从析构函数传播出去)
看代码:
#include <iostream>
using namespace std;
class DBConnection
{
public:
void close()
{
int i = 3;
int j = 0;
int k = i/j;
printf("%d\n",k);
}
};
class DBConn
{
public:
DBConn()
{
}
/*
~DBConn()
{
db.close();
}
*/
//解决办法1:强迫结束程序
~DBConn()
{
try
{
db.close();
}
catch(...)
{
abort();//如果一个程序遭遇一个“于析构期间发生的错误”后无法继续允许,强迫结束是个合理的选择。用abort阻止异常从析构函数传播出去
}
}
//解决办法2:吞下异常
/*
~DBConn()
{
try
{
db.close();
}
catch (...)
{
//制作运转记录,记下对close的调用失败
}
}
*/
private:
DBConnection db;
};
int main()
{
DBConn dbc;
//dbc对象销毁时会自动调用DBConnection的close函数;只要调用close成功,一切美好,但是如果该调用出现异常,DBConn析构函数
//会传播该异常,也就是允许它离开这个析构函数。会造成问题。
return 0;
}
/*
采用本来的析构函数:
运行程序,提示一个程序已经停止工作(感觉这里编译器优化到解决方案1了,自动做了处理),如果不停止工作,也出现了不明确行为。
采用解决方案1:
运行程序,提示一个debug error,终止程序。
采用解决方案2:
运行程序,程序运行通过,但是也不会打印k的值
*/
上面的解决方案1和2,都导致对close抛出的异常无法处理:
最佳办法就是DBConn自己重新设计一个close接口,使得程序员可以自己调用:
#include <iostream>
using namespace std;
class DBConnection
{
public:
void close()
{
int i = 3;
int j = 0;
int k = i/j;
printf("%d\n",k);
}
};
class DBConn
{
public:
DBConn()
{
}
~DBConn()
{
if (!closed)
{
try //关闭连接,如果客户不那么做的话
{
db.close()
}
catch(...)
{
//在这里结束程序或者吞下异常
}
}
}
void close()
{
db.close();
closed = true;
}
private:
DBConnection db;
bool closed;
};
int main()
{
DBConn dbc;
//dbc对象销毁时会自动调用DBConnection的close函数;只要调用close成功,一切美好,但是如果该调用出现异常,DBConn析构函数
//会传播该异常,也就是允许它离开这个析构函数。会造成问题。
return 0;
}
/*
这个方法把调用close的责任从DBConn析构函数手上移动到DBConn客户手上(但DBConn析构函数仍含有一个“双保险”调用).
如果某个操作可能在失败时抛出异常,而又存在某种需要必须处理该异常,那么这个异常必须来自析构函数以外的某个函数
因为析构函数吐出异常,总会带来“过早结束程序”或者“发生不明确行为”的风险。
这里由客户自己调用close函数,如果发现异常,可以处理。
总结:
1:析构函数绝对不要吐出异常,如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉该异常,然后吞下它们(不传播)或结束程序
2:如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通函数(而非析构函数)执行该操作。
*/
1:析构函数绝对不要吐出异常,如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕捉该异常,然后吞下它们(不传播)或结束程序
2:如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通函数(而非析构函数)执行该操作。
原文:http://blog.csdn.net/djb100316878/article/details/41047347