Item 11:Handle assigment to self in operator =
在C++中,存在一些比較隱蔽的自我賦值,如:
a[i] = a[j]或*px = *py
一般而言如果某段代碼操作pointers或references而它們被用來“指向多個相同類型的對象”,就需考慮這些對象是否爲同一個。實際上兩個對象只要來自同一個繼承體系,它們甚至不需聲明爲相同類型就可能造成“別名”,因爲一個base class的reference或pointers可以指向一個derived class對象:
class Base
{...};
class Derived:public Base{...};
void doSomething(const Base& rb,Derived* pb);//rd和*pb有可能其實是同一對象
下面是operator = 實現代碼,表面上看起來合理,但自我賦值出現時並不安全。
class Bitmap{...};
class Wiget
{
...
private:
Bitmap* pb;
}
Wiget::operator = (const Wiget& rhs)
{
delete pb; //停止使用當前的bitmap
pb = new Bitmap(*rhs.pb);//使用rhs的bitmap的副本。
return *this;
}
這裏的自我賦值問題是,operator=函數內的*this和rhs有可能是同一個對象。果真如此delete就不只是銷燬當前對象的bitmap,它也銷燬了rhs的bitmap。
傳統的解決方法是藉由operator=最前面的一個“認同測試”達到“自我賦值”的檢驗目的。
Wiget::operator = (const Wiget& rhs)
{
if(this == &rhs)return *this; //認同測試,如果是自我賦值,就不作任何事
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
然而如果是“new Bitmap“導致異常(不論是因爲分配時內存不足或是因爲Bitmap的copy構造函數拋出異常),Widget最終會持有一個指向一塊被刪除的Bitmap。這樣的指針有害,你無法安全地刪除它們,甚至無法安全地讀取它們。唯一能對它們做的安全事情是付出許多調試能量找出錯誤的起源。
只需要你注意”許多時候一羣精心安排的語句就可以導出異常安全地代碼“,這就夠了。例如一下代碼,我們只需注意在賦值pb所指東西之前別刪除pb:
Widget& Widget::operator = (const Widget& rhs)
{
Bitmap* pOrig = pb; //記住原先的pb
pb = new Bitmap(*rhs.pb);//令pb指向*pb的一個副件
delete pOrig; //刪除原先的pb
return *this;
}
現在,如果”new Bitmap“拋出異常,pb(以及棲身的那個Widget)保持原狀。即使沒有了證同測試,這段代碼還是能夠處理自我賦值。
請記住:
1.確保當對象自我賦值時operator=有良好行爲。其中技術包括比較”來源對象“和”目標對象“的地址、精心周到的語句順序、以及copy-and-swap。
2.確定任何函數如果操作一個以上對象,而其中多個對象是同一個對象時,其行爲仍然正確。