Effective C++讀書筆記之十一:在operator=中處理“自我賦值”

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.確定任何函數如果操作一個以上對象,而其中多個對象是同一個對象時,其行爲仍然正確。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章