2、構造/析構/賦值運算

條款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=中處理“自我賦值”

  1. 證同測試

    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;
    }
    
  2. 精心周到的語句順序保證異常安全性

    Widget& Widget::operator= (const Widget &rhs) {
    	Bitmap* pOrig = pb;
    	pb = new Bitmap(*rhs.bp);
    	delete pOrig;
    	return *this;
    }
    
  3. 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操作符。

  1. 複製所有local成員變量。
  2. 調用所有base classes內適當的copying函數。
  3. 不要嘗試以某個copying函數實現另一個copying函數,應該將共同機能放進第三個函數中,並由兩個copying函數共同調用。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章