Effective C++ 條款11:在operator=中處理自我賦值
阿新 • • 發佈:2021-07-01
Effective C++ 條款11:在operator=中處理自我賦值
潛在的自我賦值
// 1
a[i] = a[j]; // i = j
// 2
*px = *py; // py和px指向同一物件
// 3
class Base{};
class Derived: public Base{};
void doSomething(const Base& rb, const Derived& pd);
// rb和pd可能引用的同一個物件
自我賦值一個bug:如果類裡面有動態記憶體分配,那麼在賦值的時候,需要先delete掉原來的,再new一個新的,最後賦值。但如果是自我賦值,那麼在delete掉原來的記憶體的同時,需要賦的值也被delete了(因為都是同一塊記憶體)。
解決這個問題的方法就是在前面加個判斷
下面是個例子,假設我們有一個Bitmap類,一個Widget類。其中Widget有一個Bitmap的指標。
class Bitmap{}; class Widget { public: Widget& operator=(const Widget& rhs); private: Bitmap* pb; }; // 防止自我賦值的 Widget& Widget::operator=(const Widget& rhs){ if (this == *rhs) return *this; delete pb; pb = new Bitmap(*rhs.pb); return *this }
除此之外還有個問題。如果賦值的時候,new一塊新的空間失敗了,那麼pb會指向一塊被delete掉的空間。這樣的指標是有害的。
Widget& Widget::operator=(const Widget& rhs) {
Bitmap *tmp = pb;
pb = new Bitmap(*rhs.pb);
delete tmp;
return *this;
}
首先上面這個版本申請了一個臨時變數儲存原始的物件。然後new一個Bitmap並賦值。如果這裡出錯了,還沒到delete,其他的所有東西都保持原樣。如果沒有出錯,則再將原始的空間,通過這個臨時變數delete。這就解決了上面的問題。
然後它還取消了自我賦值的檢測。但是他依然可以處理自我檢測問題,假如兩個指標指向同一個物件,它也會先建立一個新的副本,賦值以後再刪除原來的版本。
copy and swap技術
這個技術需要保證swap函式是異常安全的,具體後面的條款會解釋,這裡只做瞭解。
Widget& Widget::operator=(const Widget& rhs) {
Widget tmp(rhs);
swap(tmp);
return this;
}
最後提一點的是,既然我們要自己建立一個rhs的副本,那為什麼不直接以值傳參,利用編譯器為我們生成一個副本呢。
Widget& Widget::operator=(Widget rhs) { // not reference
swap(rhs);
return this;
}
當然這寫法不被提倡,因為降低清晰性,讓系統變得複雜。