一、有哪些不明显的自我赋值?
a[i] = a[j]; //潜在的自我赋值
*px = * py;
- base class 和derived class
class Base{...};
class Derived :public Base{...};
void doSomething(const Base& rb,Derived* pd);
二、自我赋值存在的问题
- 可能会在停止使用之前意外释放掉
class Bitmap{...};
class Widget{
...
private:
Bitmap* pb;
};
Widget& Widget::operator=(const Widget& rhs){
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
三、如何解决
- 证同测试
Widget& Widget::operator=(const Widget& rhs){
if(this == &rhs){
return *this;
}
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
这种方式仍然存在异常安全方面的问题。如果new Bitmap抛出异常,Widget 最终会持有一个指针指向一块被删除的Bitmap。
- 使之具有异常安全性
Widget& Widget::operator=(const Widget& rhs){
Bitmap* pOrig = pb;
pb = new Bitmap(*rhs.pb);
delete pOrig;
return *this;
}
这种方式可以解决自我赋值的问题。通过将pb备份,再将*(rhs.pb)复制到pb,接着再删除pb。不会因为,rhs.pb和this->pb相同而导致先释放掉pb的问题。若new Bitmap抛出异常,pb的也指向不会改变。效率虽然不高,但是方法可行。
- copy and swap
Widget& Widget::operator=(const Widget& rhs){
Widget temp(rhs);
swap(temp);
return *this;
}
该方式的另一种变体。该种写法将copying动作从函数本体转移至函数参数构造阶段,可令编译器又是生成更高效的代码。
Widget& Widget::operator=(Widget rhs){
swap(rhs);
return *this;
}
四、总结
- 确保当对象自我赋值时operator=有良好的行为。其中技术包括比较“来源对象”和“目标对象”的地址、精心周到的语句顺序、以及copy-and-swap。
- 确定任何函数如果操作一个以上的对象,而其中多个对象是同一个对象时,其行为仍然正确。