条款08:别让异常逃离析构函数

 C++并不禁止析构函数吐出异常,但它不鼓励你这样做。

class Widget
{
public:
     ...
     ~Widget(){...}   //假设这个可能吐出一个异常
};

void doSonmething()
{
    std::vector<Widget> v;
    ...

                       //v在这里自动销毁
}

   假设v内含有10个WIdgets,在析构第一个元素时,有个异常抛出。其他九个Widget还是应该被销毁(否则它们保存的任何资源都会发生泄漏),假设调用的析构函数,又抛出一个异常。在两个异常同时存在的情况下,程序若不是结束执行就是导致不明确行为。

    假设你使用一个class负责数据库连接:

class DBConnectint{
public:
    ...
    static DBConnection create();
    void close();                    //关闭联机,失败时抛出异常
};

   为保证使用者不忘记表DBConnect对象身上调用close(),一个合理的想法是创建一个用来管理DBConnection资源的class,并在其析构函数中调用close()。

class DBConn                //这个class用来管理DBConnection对象
{
public:
    ...
    ~DBConn()               //确保数据库连接是总会被关闭
    {
        db.close();
    }
private:
    DBConnection db;
}

 这就允许使用者写下这样的代码:

{
    DBConn dbc(DBConnect::create());
                                
}

   只要调用close成功,一切都没问题。但是如果该调用导致异常,DBConn析构函数会传播该异常,也就是允许它离开这个析构函数。

  两个方法可以避免这一问题。DBConn的析构函数可以:

   1.如果close抛出异常就结束程序。通常通过调用abort完成:

DBConn::~DBConn()
{
    try {db.close();}
    catch(...)
        {
            制作运转记录,记下对close的调用失败;
            std::abort();
        }
}

 如果一个程序在析构函数期间发生错误后无法执行,强制结束程序可以阻止异常从析构函数中传播出去。

2.吞下因调用close而发生的异常:

DBConn::

DBConn::~DBConn()
{
    try{db.close();}
    catch(...)
    {
        制作运转记录,记下对close的调用失败;
    }
}

 一般而言,将异常吞掉是一个坏主意,因为它”压制了某些动作失败”的重要信息!

 这两种办法都不是很完美的做法,因为两者都无法对“导致close抛出异常”的情况作出反应。

 

  我们可以重新设计DBConn接口,使用者有机会对可能出现的问题作出反应。

class DBConn
{
public:
   ...
   void close()                //供使用者调用的函数
   {
        db.close();
        closed = true;
   }

    ~DBConn()
    {
        if (!closed)
        {
            try()
             {
               db.close();
             }
             
             catch(...)                        //如果关闭动作失败
             {                                 //记录下来并结束程序
                制作运转记录,记下对close的调用失败
                                               //或吞下异常
                ...
             }
        }
    }

privcate:
    DBConnect db;
    bool closed;
      
};

 如果某个操作可能在失败时抛出异常,而又存在某种需要必须处理该异常,那么这个异常必须来自析构函数以外的函数。

 

  结论:

   析构函数绝对不要吐出异常。如果一个析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们(不传播)或结束程序。

  如果使用者需要对某个操作函数运行期间抛出的异常作出反应,那么class应该提供一个普通函数(不是析构函数)执行该操作。

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值