什么是自我赋值,就是 v = v 这种类型的语句,也许很多人都会说鄙视这种写法,但是如下的写法会不会出现呢?
比如:a[i] = a[j]; // 不巧的是i可能和j相等
*px = *py; // 也许px和py刚好指向同一个对象呢
上述例子都是隐含的自我赋值。
再举一例:
1 class Bitmap // 位图类 2 { 3 ... 4 }; 5 6 class Widget 7 { 8 public: 9 Widget& operator=(const Widget& rhs); 10 11 private: 12 Bitmap* pb; // 指向动态分配的位图对象 13 }; 14 15 Widget& Widget::operator=(const Widget& rhs) 16 { 17 delete pb; // 释放当前位图对象 18 pb = new Bitmap(*rhs.pb); // 使用rhs中位图的副本 19 return *this; 20 }
现在假设*this和rhs是同一个对象呢?那么delete的调用就会释放*this的位图,其实同时也释放了rhs的位图资源,那么此时=的意义就变成了:让一个指针指向一个已被删除的对象,太可怕了。还好如下做法可以解决这个错误,被称为identity test:
Widget& Widget::operator=(const Widget& rhs) { if(this == &rhs) // identity test return *this; // 如果是同一个对象则返回即可 else { delete pb; // 释放当前位图对象 pb = new Bitmap(*rhs.pb); // 使用rhs中位图的副本 return *this; } }
一个更好的赋值方案是使用copy and swap技术,如下所示:
1 class Widget 2 { 3 ... 4 void swap(Widget& rhs); // 交换*this和rhs的内容 5 ... 6 }; 7 8 Widget& Widget::operator=(const Widget& rhs) 9 { 10 Widget temp(rhs); // 制作一个rhs的副本 11 swap(temp); // 将该副本内容和*this交换 12 return *this; 13 }