(一)有别名的存在,所以有可能自我赋值。
下面的代码不具备自我赋值的安全性 也不具备 异常安全性
class Bitmap {...};
class Widget {
...;
private:
Bitmap* pb;
};
Widget& Widget::operator=(const Widget& rhs) { //一份不安全的operator=实现版本
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
自我赋值的话, 调用的对象和传入的参数是同一个对象,*this 跟rhs是同一个对象。那么delete pb,那么唯一的对象就被销毁了。
在函数末尾,该对象pb发现自己持有的是一个指针指向一个已被删除的对象。
阻止这种错误的方法是(在赋值函数前面加一个”正同测试“)
class Bitmap {...};
class Widget {
...;
private:
Bitmap* pb;
};
Widget& Widget::operator=(const Widget& rhs) { //一份不安全的operator=实现版本
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
具备”自我赋值安全性“ 却不具备”异常安全性“。如果new Bitmap导致异常(无论new 内存或者copy)Widget最终会持有一个指针指向一块被删除的BItmap
解决这个问题的方法(处理 异常安全性的同时也处理自我赋值安全性)
我们只注意在复制pb所指东西之前别删除 pb。代码:
Widget& Widget::operator=(const Widget& rhs) { //一份很安全的operator=实现版本
Bitmap* pOrig = pb;
pb = new Bitmap(*rhs.pb);
delete pOrig;
return *this;
}
如图:
如果Bitmap出现异常,pb以及它所指的对象都会保持原状。
也可以自我赋值,当自我赋值的时候,如上图,pb指向原对象的一个副本。删除原bitmap。返回原对象的副本。同样实现了自我赋值。
(三)还有一个更好的方法使我们operator=函数不仅具有”自我赋值“而且”异常安全“。这就是copy and swap 没有new delete的困扰了
class Widget {
...;
void swap(Widget& rhs); //交换*this和rhs的数据。
...;
};
Widget& Widget::operator=(const Widget& rhs) {
Widget temp(rhs); //为rhs数据制作一份副本
swap(temp); //将*this数据和上述复件的数据交换。
return *this;
}
也可以const reference 为参数。还可以pass by value方式
Widget& Widget::operator=(Widget rhs) {
swap(rhs); //将*this数据和上述复件的数据交换。
return *this;
}