條款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函數被調用。這份信息通常由一個所謂vptr(virtualtable
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.
–關於賦值,你可以把它們寫出連續形式:
–同樣的,賦值採用的是右結合方法,所有上述連續賦值解析爲:
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函數共同調用。