問題聚焦:
自我賦值看似有點愚蠢的行爲,其實總會發生的
首先:它是合法的,
其次,它不必定是安全的,
再次,它有時候不是那麼明顯。
先看一個Demo
class Widget { ... };
Widget w;
...
/** 最明顯的自我賦值 **/
w = w;
/** 不那麼明顯的自我賦值 **/
// 在某個地方實現了i = j或者相同做用的事情
a[i] = a[j]
/** 潛在的自我賦值 **/
*px = *py;
/** 更爲隱蔽的自我賦值「別名」 **/
class Base { ... };
class Derived : public Base { ... };
void doSomething( const Base& rb, Derived* pd); // rb 和 *pd多是同一對象
通常而言,若是某段代碼操做指針和引用,而它們被用來指向多個相同類型的對象,就須要考慮這些對象是否爲同一個了。
自我賦值可能帶來的兩種潛在的危險:
class Bitmap { ... };
class Widget {
...
private:
Bitmap* pd;
};
/** operator=的代碼實現 **/
Widget&
Widget::operator=(const Widget& rhs)
{
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
自我賦值安全性:
若rhs和當前對象是同一對象,那麼在銷燬當前對象的pb時,把rhs的對象也銷燬了。
改進:認同測試
/** operator=的代碼實現 **/
Widget&
Widget::operator=(const Widget& rhs)
{
if (this == &rhs) return *this; // 認同測試
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
上述改進後的代碼避免了自我賦值安全性問題。
咱們來關注一下第二個問題
異常安全性:
若是new Bitmap()發生異常,那麼結果就是pb賦值直白,Widget最終持有一個指針指向一塊被刪除的Bitmap。
改進一:精心安排語句
/** operator=的代碼實現 **/
Widget&
Widget::operator=(const Widget& rhs)
{
Bitmap* pOrig = pb;
pb = new Bitmap(*rhs.pb); // 若是new Bitmap拋出異常,pb保持原狀
delete pOrig;
return *this;
}
改進方案二:copy and swap
class Widget {
...
void swap(Widget& rhs);
...
};
Widget& Widget::operator=(const Wdiget& rhs)
{
Widget temp(rhs);
swap(temp);
return *this;
}
思想:
拷貝構造函數要聲明爲「按值傳遞」
按值傳遞會形成一份副本
小結:
確保當對象自我賦值時operator=有良好的行爲。
包括:
對象來源
目標對象地址
語句順序
肯定任何函數若是操做一個以上的對象,而其中多個對象是同一個對象時,其行爲仍然正確。
參考資料:
《Effective C++ 3rd》