Effective C++ 筆記(2)構造/析構/賦值運算

0五、瞭解C++默默編寫並調用哪些函數

  (1)、默認構造函數c++

Empty(){...} //調用父類構造函數,non-static成員變量構造函數,不會默認初始化內置類型

  (2)、析構函數ide

~Empty(){...} //調用父類析構函數,non-static成員變量析構函數

  (3)、拷貝構造函數函數

Empty(const Empty& rhs){...} //單純地未來源對象的每個non-static變量拷貝到目標對象

  (4)、賦值構造函數this

Empty& operator=(const Empty& rhs){...} //單純地未來源對象的每個non-static變量拷貝到目標對象

  類內有引用、const、及父類的拷貝構造函數,賦值構造函數爲private時,編譯器會拒絕生成這一類函數。換言之,若是類內有引用、const成員變量,或者其父類的相關函數不可訪問時,必須手動生成。有指針類型變量時,存在「深拷貝、淺拷貝」問題!!!spa

class Empty:
{
public:
    Empty(){...}
    Empty(const Empty& rhs){...}
    ~Empty(){...}
    
    Empty& operator=(const Empty& rhs){...}
private:
    const int m_cInt; //只讀成員變量
    int &ref; //引用型成員變量,必須在每一個構造函數手動初始化
};

  

0六、若不想使用編譯器自動生成的函數,就該明確拒絕

禁止被拷貝構造或賦值構造的作法:設計

  (1)、將相應成員函數聲明爲private且不去實現它。(連接期出錯指針

class HomeForSale
{
public:
    HomeForSale();
    ~HomeForSale();
private:
    HomeForSale(const HomeForSale& rhs);    //只有聲明
    HomeForSale& operator=(const HomeForSale& rhs);    
};

  (2)、private繼承Uncopyable類(編譯期出錯)c++11

class Uncopyable
{
public:
    Uncopyable(){};
    ~Uncopyable(){};
private:
    Uncopyable(const Uncopyable& rhs);
    Uncopyable& operator=(const Uncopyable& rhs);
};

  

0七、爲多態基類聲明virtual析構函數

  (1)、polymorphic(帶多態性質的)base classes應該聲明一個virtual析構函數,這樣經過delete 基類指針時,也會調用其指向對象的析構函數。避免內存泄漏。若是一個class帶有一個或多個virtual函數,它就應該擁有一個virtual析構函數。code

  (2)、classes的設計目的若是不是做爲base classes使用,或不是爲了具有多態性,就不該該聲明virtual函數。(會多出一個vptr指針,佔用內存),string及STL容器均爲無virtual函數!儘可能別繼承它們作事。對象

  引伸:c++11中能夠override,final關鍵字指定。只能做用於虛函數

  override,表示此虛函數一定「重寫」了基類中的對應虛函數。  

  final,(1)做用在虛函數:表示此虛函數已處在「最終」狀態,後代類一定不能重寫這個虛函數。  

       (2)做用在類:表示此類一定不能被繼承 

  編譯器將幫你檢查是否「一定」 

 

0八、別讓異常逃離析構函數

  (1)、析構函數絕對不要拋出異常,若是一個被析構函數調用的函數可能拋出異常,析構函數應該捕捉任何異常,而後處理它們,或者結束程序;

  (2)、若是接口使用者須要對某個操做函數運行期間拋出的異常做出反應,那麼class應該提供一個普通函數(而非在析構函數中)執行該操做;

 

class DBConn
{
public:
    void close()
    {
        db.close()//可能拋出異常函數
        closed = true;
    }
    ~DBConn()
    {
        if (!closed) //若是客戶沒有主動關閉的話
        {
            try
            {
                db.close();
            }
            catch(...)
            {
                //製做運轉記錄,記下對close的調用失敗
            }
            /* code */
        }
    }
private:
    DBConnection db;
    bool closed;
};

 

0九、不要在構造和析構過程當中調用virtual函數

class Transaction //基類
{
public:
    Transaction();
    ~Transaction();
    virtual void logTransaction const = 0;
};
Transaction::Transaction()
{
    ...
    logTransaction();//記錄這筆交易
}

class BuyTransaction : public Transaction
{
public:
    BuyTransaction();
    ~BuyTransaction();
    virtual void logTransaction()const override; //記錄這筆交易
};

  BuyTransaction b;時先執行Transaction::Transaction,此時傳入的this指針爲Transaction* 故調用的是Transaction::logTransaction(),此時BuyTransaction還沒被構造出來。

  構造函數:base::base-->derive::derive

  析構函數:base::~base-->derive::~derive

十、令operator=返回一個reference to *this

  用於鏈式賦值

十一、在operator= 中處理「自我賦值」

  推薦作法:

Widget& Widget::operator=(Widget rhs)
{
    swap(rhs); //成員函數,進行交換
    return *this;
}

  (1)、確保當對象進行自我賦值時operator=有良好的行爲。包括考慮「來源對象」和「目標對象」的地址(是否爲同一個)、精心周到的語句順序、以及複製交換;

  (2)、當一個函數操做多個對象時,確保即便這些對象爲同一個對象,其行爲仍然正確。

十二、複製對象時勿忘其每個成分

  (1)、Copying函數應該確保複製「對象內的全部成員變量」及「全部base class」成分;

  (2)、不要用某個copying函數實現另外一個copying函數。應該將共同的代碼放進第三個函數中,並由兩個copying函數共同調用。

相關文章
相關標籤/搜索