1. 程式人生 > >Effective C++ 條款11:在operator中處理自我賦值

Effective C++ 條款11:在operator中處理自我賦值

     通常我們在設計一個類併為它聲明瞭賦值操作符時有一種狀況總是需要處理——自賦值,如果使用者在程式碼中注意避免自賦值當然可以但是那是很理想的狀況,在一些複雜的情況下使用者自己可能都不會意識到做了“自賦值”這個操作。例如:

     *px=*py;//潛在的自我賦值

      如果px和py恰巧指向同一個東西,這也是自我賦值。這些並不明顯的自我賦值,是“別名”帶來的結果。

我們來看一段程式碼:

class Bitmap {};
class Widget {


private: Bitmap* pb;


};
Widget& Widget::operator=(const Widget &rhs)
{

delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}

表面看上去好像合理,但是實際上並沒有真正處理了自賦值而且也不存在異常安全性

 問題在於如果*this指向的那個物件和rhs有可能是同一個物件。如果真是那樣的話delete就不只是銷燬物件的bitmap,它也把rhs的bitmap也銷燬了(因為是同一個),這樣我們的pb將會指向一個已經被刪除的物件。

如何解決這個問題呢?

①  一種普遍的做法事“認同驗證”,達到檢驗是否是同一個物件在進行賦值的目的:

Widget& Widget::operator=(const Widget&rhs)
{

if (this == &rhs)
  return *this;
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}

      這樣做的確處理了自賦值的問題,但是還有瑕疵。前一般operator=不僅不能正確處理自賦值,也不具備“異常安全性”,這個版本依然有可能存在異常方面的問題。更明確地說,如果”new Bitmap ”導致異常(不論是因為分配時記憶體不足或是因為Bitmap的拷貝建構函式丟擲異常),Widget最終會持有一個指正指向一塊被刪除的Bitmap。這樣的指標有害。你無法安全地刪除它們,甚至無法安全地讀取它們。唯一能對它們做的安全事情是付出許多除錯能量找出錯誤的起源。

     令人高興的是,讓operator=具備“異常安全性”往往自動獲得“自我賦值安全”的回報。因此愈來愈多對“自我賦值”的處理態度是傾向不去管它,把焦點放在實現“異常安全性”上,這段程式碼只要改變一下順序就能很好地處理這個問題,我們只要在

②  複製pb所指東西之前別刪除pb:

Widget& Widget::operator=(const Widget& rhs)

{
Bitmap* porig = pb;
pb = new Bitmap(*rhs.pb);
delete porig;
return *this;
}

③ copy and swap(拷貝並交換)

略  待看完條款25回來寫