類String的賦值函數比構造函數復雜得多,分四步實現:
(1)第一步,檢查自賦值。你可能會認為多此一舉,難道有人會愚蠢到寫出 a = a 這樣的自賦值語句!的確不會。但是間接的自賦值仍有可能出現,例如
// 內容自賦值
b = a;
…
c = b;
…
a = c;
// 地址自賦值
b = &a;
…
a = *b;
也許有人會說:“即使出現自賦值,我也可以不理睬,大不了化點時間讓對象復制自己而已,反正不會出錯!”
他真的說錯了。看看第二步的delete,自殺後還能復制自己嗎?所以,如果發現自賦值,應該馬上終止函數。注意不要將檢查自賦值的if語句
if(this == &other)
錯寫成為
if( *this == other)
(2)第二步,用delete釋放原有的內存資源。如果現在不釋放,以後就沒機會了,將造成內存洩露。
(3)第三步,分配新的內存資源,並復制字符串。注意函數strlen返回的是有效字符串長度,不包含結束符‘\0’。函數strcpy則連‘\0’一起復制。
(4)第四步,返回本對象的引用,目的是為了實現象 a = b = c 這樣的鏈式表達。注意不要將 return *this 錯寫成 return this 。那麼能否寫成return other 呢?效果不是一樣嗎?
不可以!因為我們不知道參數other的生命期。有可能other是個臨時對象,在賦值結束後它馬上消失,那麼return other返回的將是垃圾。
9.7 偷懶的辦法處理拷貝構造函數與賦值函數
如果我們實在不想編寫拷貝構造函數和賦值函數,又不允許別人使用編譯器生成的缺省函數,怎麼辦?
偷懶的辦法是:只需將拷貝構造函數和賦值函數聲明為私有函數,不用編寫代碼。
例如:
class A
{ …
private:
A(const A &a); // 私有的拷貝構造函數
A & operate =(const A &a); // 私有的賦值函數
};
如果有人試圖編寫如下程序:
A b(a); // 調用了私有的拷貝構造函數
b = a; // 調用了私有的賦值函數
編譯器將指出錯誤,因為外界不可以操作A的私有函數。
9.8 如何在派生類中實現類的基本函數
基類的構造函數、析構函數、賦值函數都不能被派生類繼承。如果類之間存在繼承關系,在編寫上述基本函數時應注意以下事項:
u 派生類的構造函數應在其初始化表裡調用基類的構造函數。
u 基類與派生類的析構函數應該為虛(即加virtual)