Effective c++ 学习笔记(item8)

2021-09-22  本文已影响0人  懒生活

Effective c++ 学习笔记(item8)

item8: prevent exceptions from leaving destructors.

1 为什么不能在析构函数中抛出异常

这里理解的关键是要意识当当c++正在处理异常的时候,突然又来了个"不受控制的"异常,会导致c++程序发生不可预料的问题。"不受控制"代表什么含义通过下面的例子描述。
当c++处理异常的时候,c++会暂停程序的运行,开始逆着调用顺序逐层查找异常的catch函数。如果在当前函数中没有找到catch函数,那么需要把当前函数的资源释放(包括局部对象的销毁),然后转到更上一层去查找crath函数。找到后执行,然后代码从相关联的最后一个catch块后面的语句开始继续。如果一直没有找到,程序就会直接终止。这里隐藏的问题场景是,当处理中断时,发现本层内没有catch函数,然后释放本层内的局部对象,万一某个局部对象在释放(调用他的析构函数)时又抛出了异常,且这个异常并没有被析构函数自己处理(就是所谓的不受控制)这时就导致了异常处理时又撞见异常的局面,c++就无能为力了。如果析构函数抛出的异常被自己"处理掉既吞下", 那就不会有冲突。
通过下面的例子可以很好的理解这个问题

class ExceptionObj
{
public:
~ExceptionObj()
{
throw 1;
}
}
int test()
{
ExceptionObj aExcption;
throw 2; //在这里抛出的异常没有找到异常处理函数,所以
//会退出该函数,并释放局部变量aExcption.
//调用aExcption的析构函数时,析构函数又抛出了异常,且这个异常没有被处理
//逃出了析构函数和当前的异常一起竞争使用异常的追溯能力。这样子程序就崩溃了
}
int main()
{
try
{
test();
}
catch (int e)
{
cout<<"exception "<<e<<endl;
}
cout<<"continue"<<endl;
return 0;
}

如果把析构函数中的异常自己吞下,就不会有竞争导致程序崩溃,改写如下

~ExceptionObj()
{
    try
{throw 1;}
catch(int e)
{cout<<"swallow my own exception"<<endl;}
}

2 如果析构函数不可避免要抛出异常怎么办

在析构函数中捕获并处理该异常。不要让异常跑出析构函数。 紧接着这个话题,Scott介绍了一个编程技巧。如果析构函数中会抛出异常,那么把抛出异常的部分提炼成public函数,让用户可以调用,这样子用户可以第一时间处理异常。但是为了避免用户意外漏了调用,在析构函数中还是要继续调用这部分可能抛异常的代码,同时要保证捕获。
比如下面的代码

DBConn::~DBConn()
{
    try {db.close()}
    catch(...){}
}

一个数据库连接对象DBConn在析构的时候,希望能够自动关闭连接,但关闭连接的时候可能会抛出异常。上面在析构函数内部捕获异常的实现毫无疑问是正确的。但更好的是可以提供一个接口,让用户自己去调用,并让用户自己意识到要处理可能抛出的异常。而用户如果忘记了调用和处理异常,我们通过保留的析构函数上的关闭实现,实现双保险。示意代码如下

DBConn::mannualClose()
{db.close(); isClosed = true;}

DBConn::~DBConn()
{
    if(isClosed == false)
    {
        try {db.close()}
        catch(...){}
    }
}
上一篇下一篇

猜你喜欢

热点阅读