Effective C++条款08:构造/析构/赋值运算之(别让异常逃离析构函数)

一、析构函数也会抛出异常

  • C++并不禁止析构函数抛出异常,但是不建议这样

演示案例

class Widget
{
public:
    ~Widget() {} //假设这个析构函数可能会抛出异常
};

int main()
{
    std::vector<Widget> v;
    return 0;
}//v在这里自动销毁
  • 假设v内有10个Widgets,那么在程序结束时会逐个释放这10个Widget对象
  • 但是假设在释放第1个对象时,第1个Widget的析构函数中抛出了异常,并且没有对任何异常进行任何处理,此时程序就会中断

二、通过例子来告知如何处理异常

  • 现在建立以下两个类,一个类负责连接数据库,另一个用来管理数据库对象
class DBConnection
{
public:
    //该函数返回一个DBConnection对象
    static DBConnection create();

    //关闭数据库连接(失败会抛出异常)
    void close();
};

//用来管理DBConnection对象
class DBConn
{
public:
    //确保数据库连接总是会被关闭
    ~DBConn() { db.close(); }
private:
    DBConnection db;
};
  • 如果此时客户写出了这样的代码:
int main()
{
    //建立一个DBConnection对象并交给DBConn管理,使用DBConn的接口管理DBConnection
    DBConn dbc(DBConnection::create());

    return 0;
}//程序结束时,DBConn对象被销毁,因此会自动为DBConnection对象调用close
  • 如果调用close()调用成功的话那么就没有什么事。如果close()函数调用出错(有异常),那么DBConn析构函数也会传播该异常,导致程序出错

两种普通的解决办法

  • 解决办法①:
    • 如果close函数抛出异常,就结束程序,可以通过调用abort完成
    • 如果程序在析构期间发生一个错误,那么“强迫程序结束”是一个合理的设置
DBConn::~DBConn()
{
    try { db.close(); }
    catch (...) {
        //此处还可以做一个记录,记下对close的调用失败
        std::abort();
    }
}
  • 解决办法②:
    • 忽略(吞掉)这个异常
    • 一般而言,忽略这个异常是个坏主意,因为忽略这个异常会造成不明确的行为
DBConn::~DBConn()
{
    try { db.close(); }
    catch (...) {
        /*此处还可以做一个记录,记下对close的调用失败,
          其他什么都不做
        */
    }
}

一个更好的解决办法

  • 一个更好的策略是重新设计DBConn接口,DBConn可以追踪所管理的DBConnection是否已经关闭
    • 如果已经关闭就不做任何事情
    • 如果还没关闭,并且抛出了异常,那么还是要使用到上面的两种解决方案
class DBConn
{
public:
    DBConn::~DBConn()
    {
        if (!closed) {
            try { db.close(); }
            catch () {
                //此处还可以做一个记录,记下对close的调用失败
            }
        }
    }

    void close() {
        db.close();
        closed = true;
    }
private:
    DBConnection db;
    bool closed;
};

三、总结

  • 析构函数绝对不要抛出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕获并处理该异常
  • 如果客户需要对某个操作函数运行期间抛出的异常做出反应,那么类应该提供一个普通函数(而非在析构函数中)执行该操作
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

董哥的黑板报

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值