zoukankan      html  css  js  c++  java
  • [Effective C++ --008]别让异常逃离析构函数

    这章非常容易理解:因为C++并不禁止析构函数吐出异常,只是不鼓励这样做而已。

    一、原因

    假设我们有10个装着鸡蛋的容器,而且现在我们还想着把它在析构函数打烂。

    class Egg {
    public :
         ...
         ~Egg() {
              // 这里可能出错,导致蛋打不烂
         }
    };
    
    void foo() {
          vector<Egg> v   // 假设v中间有10个Egg 
          ....
    }                     // v在这里被自动销毁

     如果我们在销毁10个鸡蛋的过程中,在析构第一个鸡蛋的时候,有个异常被抛出,按照销毁机制,后续的9个鸡蛋还是需要被销毁的(否则鸡蛋保存的任何资源都会发生泄漏)。

    但是如果后面的鸡蛋仍然抛出异常,在两个异常同时存在的情况下,C++程序会结束执行或者出现不明确的行为。

    就算是使用STL的其他容器,还是会发生同样的问题。

    为什么呢?因为C++不鼓励析构函数吐出异常。

    二、详解

    为了方便上面的原因理解,我们可以来尝试一下的例子:

    class DB {
    public :
         ....
         static DB create() ;//函数返回DB对象
         void close();        //关闭数据库的联机,失败则抛出异常    
    }

    如果为了方便其他人员使用DB类,防止在调用DB的时候忘记关闭连接,那么我们可以贴心一下:

    class DBC {
    public :
         ....
         ~DBC() {       // 确保每次调析构的时候都会关闭连接         
           db.close();    
        }
    private:
         DB db;
    }

    其他人直接使用DBC的类就好了,但是如果这样写,就会出现章一种的问题了,如果析构中抛出了异常怎么办?

    我们可以用两种方法来解决:

    方法1:

    DBC::~DBC() {
          try {(db.close();) }  //检查异常 
          catch (...) {
                std::abort();      //如果catch到了异常,那么直接强迫结束程序
          }
    }

    方法2:

    DBC::~DBC() {
          try {(db.close();) }  //检查异常 
          catch (...) {
                ... //如果catch到了异常,记录对close调用失败
          }
    }

    上面两种方法似乎都会异常进行了"提示",但是都无法针对“导致异常”的情况作出处理。

    因此,我们可以考虑重新设计DBC类:

    class DBC {
    public :
         ....
         void close() {
              db.close();
              closed = true;
         }
         ~DBC() {       // 确保每次调析构的时候都会关闭连接         
              if (!closed) {
                   try {db.close();}
                   catch(...) {
                        ...// 记录异常
                   }
              }
        }
    private:
         DB db;
         bool closed;
    }

    ■总结:

    1.析构函数绝对不要吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该要能捕捉任何异常,然后“吞下异常”或者终止程序。

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

  • 相关阅读:
    c++/c语言中如何调用DLL
    fortran出现stack overflow的原因及解决办法
    iOS: ARC和非ARC下使用Block属性的问题
    Objective-C Autorelease Pool 的实现原理
    class-dump 复制到/usr/bin目录不可写,Operation not permitted 解决办法
    Auto Layout 使用心得
    iOS-关于微信支付
    IOS应用安全(五):高级Runtime分析和操作
    Objective-C Runtime 运行时之六:拾遗
    Objective-C Runtime 运行时之五:协议与分类
  • 原文地址:https://www.cnblogs.com/hustcser/p/4090217.html
Copyright © 2011-2022 走看看