Effective C++ —— 别让异常逃离析构函数

C++ 并不禁止析构函数吐出异常,但不鼓励这么做。
1.如果一个被析构函数调用的函数可能抛出异常,则析构函数应该捕获这些异常,然后终止程序或吞下它们。
2.如果客户需要对某个函数在运行中抛出的异常做出反应,那么这个类应该提供一个普通函数(非析构函数)执行该操作。

class Widget
{
    public:
    Widget();
    ~Widget()
    {}
};
void work()
{
    std::vector<Widget> v;
}

v在销毁时有责任销毁容器内的所有Widget 对象,如果在销毁第一个对象时抛出了异常,剩下的对象还是应该被销毁。如果第二个对象被销毁时又出现了异常,这时对于C++而言异常有些多了,程序不是结束执行就是导致不明确行为。
使用标准库容器或数组等都有可能出现这样的情况,其实只要析构函数吐出异常,程序都会出现这样的问题。
第二个例子:DBConnection 类负责数据库连接
class DBConnection
{
public:
static DBConnection Create();
private:
void close();//关闭联机,释放资源,失败则抛出异常
};

为确保客户不忘记调用close()方法来释放资源,可以设计一个数据库资源管理类,在其析构函数中调用close().
class DBResManager
{
public:
DBResManager();
~DBResManager()
{
db.close();
}
private:
DBConnection db;
};
close()调用成功,一切都美好,一旦异常抛出,则DBResManager析构函数会传播异常。两个解决方案:
一.调用abort()终止程序

DBResManager::~DBResManager()
{
try
{
    db.close();
}
catch()
{
    std::abort();//终止程序,避免异常传播
}
}

二.吞下异常

DBResManager::~DBResManager()
{
try
{
    db.close();
}
catch()
{
    //吞下异常,程序继续执行,保证程序可以继续可靠执行
}
}

最好的方案是:重新设计DBResManager类的接口,让客户有机会处理异常

class DBResManager
{
    public:
    DBResManager();
    void close()
    {
        db.close();
        closed = true;
    }
    ~DBResManager()
    {
        if(!closed)
        {
            try
            {
                db.close();
            }
            catch()
            {
                //吞下异常,程序继续执行,保证程序可以继续可靠执行
            }       
    }
    private:
    DBConnection db;
    bool closed;
};

说明:当某个操作在失败时可能抛出异常,又必须对这个异常进行处理时,那么这个处理函数必须是非析构函数。因为在析构函数中抛出异常是个很糟糕的想法,结果就是过早地结束程序或发生不明确的行为。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值