Effective C++讀書筆記(二)構造、析構、賦值

條款05:瞭解C++默默編寫並調用哪些函數
                Know whatfunctions C++ silently writes and calls

C++會爲默認的空類(emptyclass)添加

                      •Default 默認構造函數
                      •Copy 構造函數
                      •析構函數
                      •Copy assignment 複製賦值操作符
     –唯有這些函數被調用時,它們纔會被編譯器創建出來,且都是public && inline

條款06:若不想使用編譯器自動生成的函數,就該明確拒絕
                 Explicity disallow the use of compiler-generated functions you to notwant.

方法:

               •可以自定義編譯器會默認生成的函數,手動定義成private,並且不予實現;
               •或者使用空基類(emptybase class)聲明空函數來繼承。

條款07:爲多態基類聲明Virtual析構函數
                Declare destructors virtual in polymorphic base classes.


C++明確指出,當derivedclass對象經由一個base class指針被刪除,而該baseclass帶着一個non-virtual析構函數,其結果未有定義----實際執行時通常發生的是對象的derived成分沒被銷燬。

                         •方法:base classes定義一個virtual析構函數

任何class只要帶有virtual函數都幾乎確定應該有一個virtual析構函數。

欲實現出virtual函數,對象必須攜帶某些信息,主要用來在運行期決定哪一個virtual函數被調用。這份信息通常由一個所謂vptrvirtualtable pointer指針指出。


class帶一個purevirtual(純虛)析構函數會導致abstract(抽象)classes ---也就是不能被實體化(instantiated)class.


總結

                •Polymorphic(帶多態性質的)baseclasses應該聲明一個virtual析構函數。如果class帶有任何virtual函數,它就應該擁有一個virtual析構函數。
                •Classes的設計目的如果不是作爲baseclasses使用,或不是爲了具備多態性(polymorphic),就不該聲明Virtual析構函數。

條款08:別讓異常逃離析構函數
                 Preventexception from leaving destructors


當析構函數發生異常時,有以下2中解決辦法

           •如果拋出異常就結束程序,通常通過調用 abort完成

DBConn::~DBConn()
{
Try{ db.close();}
Catch(…){
    std::abort();
}
}


          •吞下因調用析構函數而發生的異常;

DBConn::~DBConn()
{
Try{ db.close();}
Catch(…){

}
}


總結

                    •析構函數絕對不要吐出異常。如果一個被析構函數調用的函數可能拋出異常,析構函數應該捕捉任何異常,然後吞下它們(不傳播)或結束程序。

                    •如果客戶需要對某個操作函數運行期間拋出的異常做出反應,那麼class應該提供一個普通函數(而非在析構函數中)執行該操作。


條款09:絕不在構造和析構過程中調用Virtual函數
               Nevercall  virtual functions duringconstruction or destruction


Base class構造期間virtual函數絕不會下降到derived classes階層。(在base class構造期間,virtual函數不是virtual函數)


條款10:令operator =返回一個referenceto *this
                 Haveassignment operator return a reference to *this.


關於賦值,你可以把它們寫出連續形式:

intx,y,z;
x = y = z =15;

同樣的,賦值採用的是右結合方法,所有上述連續賦值解析爲:

                  x =(y = ( z = 15));

爲了實現連續賦值,賦值操作符必須返回一個reference指向操作符的左側實參。


條款11:在operator=中處理自我賦值
                Handleassignment to self in operator=.


確保當對象自我賦值時 operator=有良好的行爲。其中技術包括比較來源對象目標對象的地址、精心周到的語句順序、以及copy-and-swap

class Widget{
...
void swap(Widget& rhs);
...
};
Widget& Widget::operator=(const Widget& rhs)
{
     Widget temp(rhs);
     swap(temp);
     return *this;
}
也可以將copying動作移至參數構造階段

Widget Widget::operator=(Widget rhs)
{
 swap(rhs);
 return *this;
}
爲了伶俐巧妙的修補而犧牲了清晰性。

確定任何函數如果操作一個以上的對象,而其中多個對象是同一個對象時,其行爲仍然正確。


條款12:複製對象時勿忘其每一個成分
                 Copy allparts of an object


當你編寫一個copying函數,請確保

         (1) 複製所有local成員變量,
         (2) 調用所有baseclasses內的適當的copying函數。

Copying 函數應該確保複製對象內的所有成員變量所有baseclass成員

不要嘗試以某個copying函數實現另一個copying函數。應該將共同功能放進第三個函數中,並由兩個copying函數共同調用。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章