在 operator= 中處理自我指派
自我指派發生在對象被指派給自己時,比如w=w,但是更多的是隐性的自我指派(不能一下子看穿的)
如果i=j,那麼a[i]=a[j]算不算自我指派呢
假設有這麼一段代碼
class Bitmap {...}
class Widget
{
public:
...
private:
Bitmap* pb;
}
//下面是operator=的代碼
Widget& Widget::operator=(const Widget& rhs)
{
delete pb;
pb=new Bitmap(*rhs.pb);
return *this
}
這段operator=的代碼沒有考慮到this==&rhs
當this==&rhs時,pb早已經被釋放了,這樣new Bitmap就會因為發現異常而不會生效,其結果是this->pb指向了一個已經删除的對象
解決方法很簡單:加個證同測試
Widget& Widget::operator=(const Widget& rhs)
{
if(this==&rhs)
return *this;
delete pb;
pb=new Bitmap(*rhs.pb);
return *this
}
這個方法基本可行,但還是會存在問題,當new操作發生異常時(此時代碼就不會往下執行),pb還是會指向一個已經被删除了的對象,這說明這種方法沒有”異常安全性”
解決方法是,不考慮證同測試(證同測試會降低代碼執行速度),new之後再删除原來的指針
就像下面這樣
Widget& Widget::operator=(const Widget& rhs)
{
Bitmap* pOrg=pb;//儲存原始的this指針
pb=new Bitmap(*rhs.pb);//将新的對象指派給this
delete pOrg;//删除原來的指針
return *this
}
這個代碼的好處是具備”異常安全性”和”自我指派安全性”
1.當new操作發生問題時,operator=将會停止往下執行,pb會維持原來的值
2.當this==&rhs時,我們忽略它們是同一個對象的事實,建立一份rhs的副本賦給this,然後删除原始的this對象
當然還有更”騷”的操作:參數傳入時byValue,我們知道,byValue時,形參會複制實參的資料,這樣就把建立副本的工作交給了構造函數,比如下面這樣
Widget& Widget::operator=(const Widget rhs)
{
Bitmap* pOrg=pb;//儲存原始的this指針
pb=&rhs;//将新的對象指派給this
delete pOrg;//删除原來的指針
return *this;
}