【讀書筆記】Effective C++(02)構造/析構/賦值

做者:LogM數據庫

本文原載於 https://segmentfault.com/u/logm/articles,不容許轉載~segmentfault

2. 構造/析構/賦值

  • 2.1 條款05:C++會自動編寫default構造、拷貝構造、析構、賦值函數

    //你覺得你寫了個沒有代碼的空類
    class Empty{};
    
    // 實際上,C++自動生成了不少函數
    class Empty{
    public:
        Empty() {...}    //默認構造函數
        Empty(const Empty& rhs) {...}    //拷貝構造函數
        ~Empty() {...}     //析構函數
    
        Empty& operator=(const Empty& rhs) {...}    //賦值函數
    };
  • 2.2 條款06:聲明爲private防止自動生成的函數被使用

    //把拷貝構造函數和賦值函數聲明爲private類型,防止被使用
    class Empty {
    public:
        ...
    private:
        ...
        Empty(const Empty&);
        Empty& operator=(const Empty& rhs);
    };
  • 2.3 條款07:使用多態特性時,基類的析構函數必須爲virtual

    //一個多態的場景
      class TimeKeeper {   //計時器(基類)
      public:
          TimeKeeper();
          virtual ~TimeKeeper();
          ...
      };
      class AtomicClock: public TimeKeeper {...}    //原子鐘
      class WristWatch: public TimeKeeper {...}    //腕錶
    
      //每每這麼使用多態特性
      TimeKeeper* ptk = getTimeKeeper();
      ...
      delete ptk;

    上面是使用多態的一個場景,當delete ptk時,由於ptkTimeKeeper類型,若是基類析構函數不是virtual,那麼ptk的析構時只調用基類析構函數,析構不正確;使用virtual ~TimeKeeper();保證ptk析構時調用正確的子類析構函數函數

    若是一個類帶有virtual函數,說明這個類有可能用於多態,那麼就應該有virtual析構函數。

    不要對non-virtual析構函數的類使用多態特性,包括string、vector在內的STL容器。this

    帶有virtual函數的類會生成vtbl (virtual table)用於在運行期間肯定具體調用哪一個函數,由vptr (virtual table pointer)指向,佔用空間。胡亂聲明virtual會增長體積。code

  • 2.4 條款08:析構函數不要拋出異常

    • 析構函數拋出異常,程序終止析構,會致使資源沒有徹底釋放。
    //管理數據庫鏈接的典型場景
        //若是這麼寫,析構函數有可能拋出異常
        class DBConn {
        public:
            ...
            ~DBConn() {
                db.close();
            }
        private:
            DBConnection db;
        }
    
        //在析構函數內捕捉異常,不要讓析構函數的異常拋出
        class DBConn {
        public:
            ...
            ~DBConn() {
                try { db.close(); }
                catch (...) {
                    ...
                }
            }
        private:
            DBConnection db;
        }
  • 2.5 條款09:不在構造和析構函數中使用virtual函數

    • 構造函數在構造時,派生類的成員變量未初始化完畢,virtual函數指向基類;
    • 析構函數在析構時,派生類的成員變量部分析構,virtual函數指向基類。
  • 2.6 條款10:令 operator= 返回值爲 reference to *this

    • 這麼作的目的是爲了實現連續賦值
    int x, y, z;
    x = y = z = 1;   //連續賦值
    
    int& operator=(const int& rhs) {
        ...
        return *this;   //返回左側對象的引用
    }
  • 2.7 條款11:在 operator= 中處理自我賦值

    //下面代碼,若是出現自我賦值,則出錯
    Widget& Widget::operator=(const Widget& rhs) {
        delete elem;
        elem = new Bitmap(*rhs.elem);
        return *this;
    }
    
    //方法1
    Widget& Widget::operator=(const Widget& rhs) {
        if (this == &rhs) { return *this; }
    
        delete elem;
        elem = new Bitmap(*rhs.elem);
        return *this;
    }
    
    //方法2
    Widget& Widget::operator=(const Widget& rhs) {
        Bitmap* pOrig = elem;
        elem = new Bitmap(*rhs.elem);
        delete pOrig;
        return *this;
    }
  • 2.8 條款12:拷貝構造和賦值函數中不要遺漏成員變量

    • 編譯器不檢查拷貝構造和賦值函數是否對全部成員變量進行了拷貝;
    • 派生類的拷貝構造和賦值函數記得要先調用基類的拷貝構造和賦值函數。
相關文章
相關標籤/搜索