1、c++禁止析构函数吐出异常,但也不鼓励这样做。
来看实例:
class Widget
{
public:
Widget();
~Widget()
{
}
};
void g_doSomething()
{
vector<Widget> arr;//arr为临时对象,函数结束自动销毁。
}
在 g_doSomething()中,析构函数在销毁资源的过程中吐出了一个异常,没有办法继续释放其他的资源,就会造成资源泄漏的严重后果。
所以要求我们不能够让异常逃离析构函数,至少要在析构函数遇到异常的时候能够终止程序,或者让它吞下异常。
2、那么如果你的析构函数必须执行一个动作,而且该动作可能在失败时候抛出异常,怎么办?
举个列子,class DBConnection负责数据库的连接:
class DBConnection
{
public:
static DBConnection* Create() //返回创建的DBConnection对象。
{
return new DBConnection;
}
void close() //关闭联机,失败抛出异常。
{
}
};
其中一个合理的解决方案是创建一个用来管理的DBConnecton资源的class DBConnectMgr,并在析构函数中调用close。例如:
class DBConnectMgr //用来管理DBConnection对象
{
public:
DBConnectMgr(DBConnection* item)
{
m_db = *item;
}
~DBConnectMgr()
{
m_db.close();
}
private:
DBConnection m_db;
};
void g_doSomething2()
{
DBConnectMgr dbm(DBConnection::Create());//创建DBConnection对象并且交给DBConnectMgr对象管理。
//... 函数结束,需要销毁DBConnectMgr对象而调用~DBConnectMgr(),自动调用m_db.close()销毁DBConnection对象。
}
3、只要调用DBConnection 对象的close()调用成功,就大功告成。
但是如果该调用导致异常,DBConnectMgr析构函数就会传播异常,导致错误离开DBConnection析构函数,抛出难以驾驭的问题。
怎么避免这个问题?
可以修改DBConnectMgr类。不让析构函数来抛出异常,让用户自己来处理自己抛出的异常,避免因为析构函数抛出异常而导致出现不明的行为。
class DBConnectMgr //用来管理DBConnection对象
{
public:
DBConnectMgr(DBConnection* item)
{
m_db = *item;
}
~DBConnectMgr()
{
if (!m_bClosed)
{
try
{
m_db.close();
}
catch (...) //close失败,就去记录异常处或者吞下异常。
{
//WriteLog() 记下close()调用失败。
abort(); // 吞下异常
}
}
}
public:
void Close()
{
m_db.close();
m_bClosed = true;
}
private:
DBConnection m_db;
bool m_bClosed;
};
4.小结。
4.1、析构函数绝对不要吐出异常,如果调用析构函数可能会抛出异常,析构函数应该catch任何的异常,吞下他们(不传播),或者结束程序。
4.2、如果客户要对某个操作函数运行期间的异常作出反应,那么class应该提供了另一个普通的函数(而非在析构函数中)执行该操作。
4.3、不能在析构函数中调用可能出现异常的函数,因为析构函数有可能多次被调用,抛出异常可能会出现不可预知的后果。
4.4、如果想处理该异常,就应该自己定义一个普通的函数来处理该异常,比如自己写一个接口,让用户自己控制。
参考文章:https://www.cnblogs.com/MrZHj/p/4519707.html
入门萌新,浅知拙见,若有斧正,不胜感激。^ - ^