Effective C++(11) 自我賦值(a=a)時會發生什麼?

問題聚焦:
自我賦值看似有點愚蠢的行爲,其實總會發生的
首先:它是合法的,
其次,它不必定是安全的,
再次,它有時候不是那麼明顯。

先看一個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》
相關文章
相關標籤/搜索