文章目錄
條款05:瞭解C++默默編寫並調用哪些構造函數
編譯器會自動生成,並且僅當生成的代碼合法以及這些函數會被調用時,纔會被創建出來:
(1) 默認構造函數
(2) copy構造函數
(3) 析構函數
(4) 賦值操作符(=):內含reference成員、內含const成員、基類的operator=爲private,這三種情況不會自動生成=操作符。
條款06:若不想使用編譯器自動生成的函數,就該明確拒絕
1、將成員函數聲明爲private而且故意不實現他們(member函數或者friend函數調用會導致link報錯)
class HomeForSale {
public:
...
private:
...
HomeForSale(const HomeForSale &);
HomeForSale& operator= (cons HomeForSale &);
};
2、設計一個不允許copying動作的基類
class Uncopyable {
protected:
Uncopytable() {}
~Uncopytable() {}
private:
Uncopytable (const Uncopytable &);
Uncopytable& operator= (const Uncopytable &);
};
條款07:爲多態基類聲明virtual析構函數
虛函數是指一個類中希望重載的成員函數,當用一個基類指針或引用指向一個繼承類對象的時候,調用虛函數實際上調用的是繼承類的版本。
當繼承類對象經由基類指針被刪除,並且基類的析構函數是non-virtual,結果未定義,實際執行時通常發生的是對象的繼承部分沒有被銷燬。所以對於帶多態性質的基類應該要聲明一個virtial析構函數,反之則不該聲明virtual析構函數(由於虛函數表的存在,會使對象體積增加)。
條款08:別讓異常逃離析構函數
析構函數不要吐出異常,否則要應該要對異常進行捕捉,並處理或結束程序。
考慮下述代碼,當dosomething()函數結束時,會逐個調用vector v中每個Widget對象的析構函數,假設有兩個Widget對象的析構拋出異常,就會導致不明確行爲。
class Widget {
public:
...
~Widget() {...}
};
void dosomething()
{
std::vector<Widget> v;
...
}
條款09:絕不在構造和析構過程中調用virtual函數
base class構造期間virtual函數絕不會下降到derived class階層,即在base class構造期間,virtual函數不是virtual函數。因爲在derived class對象的base class構造期間,對象類型是base class,而不是derived class;析構函數也是同理,derived class對象的析構函數先調用,然後觸發base class對象的析構函數,此時對象的類型也是base class。
class Transaction {
public:
Transaction();
virtual void logTransaction() const = 0;
...
};
Transaction::Transaction() {
...
logTransaction();
}
class SellTransaction: public Transaction {
public:
virtual void logTransaction() const;
...
};
SellTransaction s; // base class的構造函數調用的是base class的logTransaction
條款10:令operator=返回一個reference to *this
賦值動作採用右結合律,即
int x, y, z;
x = y = z = 15; //-->
x = ( y = ( z = 15));
所以爲了實現這種連鎖賦值,賦值操作符必須返回一個reference指向操作符的左側實參:
class Widget {
public:
...
Widget& operator= (const Widget &rhs) {
...
return *this;
}
};
條款11:在operator=中處理“自我賦值”
-
證同測試
class Bitmap {...}; class Widget { ... private: Bitmap *bp; }; Widget& Widget::operator= (const Widget &rhs) { if (this == &rhs) return *this; delete pb; pb = new Bitmap(*rhs.pb); return *this; }
-
精心周到的語句順序保證異常安全性
Widget& Widget::operator= (const Widget &rhs) { Bitmap* pOrig = pb; pb = new Bitmap(*rhs.bp); delete pOrig; return *this; }
-
copy and swap
class Widget {
...
void swap(Widget& rhs);
...
};
Widget& Widget::operator= (const Widget &rhs) {
Widget tmp(rhs);
swap(tmp);
return *this;
}
條款12:複製對象時勿忘其每一個成分
copying函數就是copy構造函數和copy assignment操作符。
- 複製所有local成員變量。
- 調用所有base classes內適當的copying函數。
- 不要嘗試以某個copying函數實現另一個copying函數,應該將共同機能放進第三個函數中,並由兩個copying函數共同調用。