問題聚焦:
自我賦值看似有點愚蠢的行為,其實總會發生的 首先:它是合法的, 其次,它不一定是安全的, 再次,它有時候不是那麼明顯。先看一個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; }
class Widget { ... void swap(Widget& rhs); ... }; Widget& Widget::operator=(const Wdiget& rhs) { Widget temp(rhs); swap(temp); return *this; }
拷貝構造函數要聲明為“按值傳遞” 按值傳遞會造成一份副本
確保當對象自我賦值時operator=有良好的行為。
包括:
對象來源目標對象地址語句順序確定任何函數如果操作一個以上的對象,而其中多個對象是同一個對象時,其行為仍然正確。