zoukankan      html  css  js  c++  java
  • Item 8:析构函数不要抛出异常 Effective C++笔记

    Item 8: Prevent exceptions from leaving destructors.

    析构函数不要抛出异常

    因为析构函数经常被自己主动调用,在析构函数中抛出的异常往往会难以捕获,引发程序非正常退出或没有定义行为。 比如,对象数组被析构时。会抛出多于一个的异常,然而同一时候存在的异常在C++标准中是禁止的, 因此程序会非正常退出:

    class Widget {
    public:
      ~Widget() { ... }            // assume this might emit an exception
    };
    void doSomething(){
      std::vector<Widget> v;
    }                              // v is automatically destroyed here
    

    事实上,容器中的对象在析构时抛出异常还会引起兴许的对象无法被析构。导致资源泄漏。 这里的资源能够是内存,也能够是数据库连接。或者其它类型的计算机资源。

    析构函数是由C++来调用的,源码中不包括对它的调用,因此它抛出的异常不可被捕获。 对于栈中的对象而言,在它离开作用域时会被析构。对于堆中的对象而言。在它被delte时析构。

    请看:

    class C{
    public:
        ~C(){ throw 1;}
    };
    void main(){
        try{
            C c;
        }
        catch(int e){}
    }
    

    析构的异常并不会被捕获。由于try{}代码块中仅仅有一行代码C c。它并未抛出异常。 经Homebrew gcc 5.1.0编译后,执行时会产生这种错误输出:

    libC++abi.dylib: terminating with uncaught exception of type int
    

    或许你认为在try中用delete手动释放堆对象就能够捕获异常。我们来试试:

    C *p = new C;
    try{
        delete p;
    }
    catch(int e){}
    

    上述代码会给出相同的错误输出:

    libC++abi.dylib: terminating with uncaught exception of type int
    

    这仅仅能说明delete并非对析构函数的直接调用,它仅仅是一个keyword。

    析构函数还是由C++调用的。 其实。假设上面不delete的话,程序不会产生错误,此时p属于内存泄露。 这些内存是在程序退出后由操作系统来回收的。

    那么在析构函数中。应处理掉可能的异常。保证对象可以被完整地释放。 由于析构函数中总会出现非安全的代码,我们仅仅能吞掉异常,或者退出程序。这样:

    class DBConn{
    public:
        ~DBConn{
            if(!closed){
                try{
                    db.close();
                }
                catch(...){
                  cerr<<"数据库关闭失败"<<endl;
                  // 或者直接退出程序
                    // std::abort();
                }
            }
        }
    private:
        DBConnection db;
    };
    

    另外值得一提的是,上述catch(...)中的...并非省略号,它是合法标识符,表示不确定的形參。

    可是对于一个完好的设计,我们须要让客户知道这里发生了异常。

    在此仅仅需为不安全语句提供一个新的函数。在析构函数中我们还是运行默认操作(忽略、记录、或者结束程序)。

    class DBConn{
    public:
        void close(){
            db.close();
        }
        ...
    

    这个常规方法给了客户自行关闭数据库并处理异常的机会。当然假设他放弃这个机会, 便不能怪罪于我们让程序退出或者吞掉异常了。

  • 相关阅读:
    .Net Core自动化部署系列(一):Jenkins + GitLab
    经典案例复盘——运维专家讲述如何实现K8S落地(摘抄)
    Quartz系列(一):基础介绍
    生产环境项目问题记录系列(二):同步方法调用异步方法
    微服务理论系列(一):服务发现四问四答(摘抄)
    Java中的继承、封装、多态的理解
    Java三大主流框架概述
    面试的技巧
    myBaits持久性框架
    MyBaits框架入门总结
  • 原文地址:https://www.cnblogs.com/jhcelue/p/6918821.html
Copyright © 2011-2022 走看看