条款11:在operator=处理自我赋值
自我赋值发生在对象被赋值给自己:
class Widget {
...
}
Widget w ;
…
w = w // 赋值给自己
还有一个隐含的会发生自我赋值的例子:
a[i] = a[j];//当i = j时
下面有这样一个类:
class Bitmap{};
class Widget {
...
private :
Bitmap *pb;
};
l 版本一
Widget& Widget::operator= (const Widget& rhs){
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
注意:这里存在一个问题,那就是如果rhs所指的对象就是this。Delete pb后,就把this的pb删除了,所以不行。
欲阻止这样的错误,可以加上一个证同测试:
l 版本二
Widget& Widget::operator= (const Widget& rhs){
if(this == rhs) return *this; // 证同测试
delete pb;
pb = new Bitmap(*rhs.pb);
return *this;
}
这样做法是可以的,但是还有一个问题是如果在new Bitmap时,出现的异常,则会使widget对象最终会持有一个指针指向一块被删除的bitmap。这样的指针是有害的,你无法安全的删除它们,也无法安全的读取它们。
但是精心安排一下,是可以避免这个错误的:
l 版本三:
Widget& Widget::operator= (const Widget& rhs){
Bitmap *obj = rhs.pb;
pb = new Bitmap(*rhs.pb);
delete obj;
return *this;
}
现在如果new Bitmap出现的异常,pb也会保持原状。注意,虽然我们没有加入证同测试,但是上面的代码也可以处理自我赋值现象。
版本四:
针对版本三还有一个替换版本:
class Widget {
public:
...
swap(Widget& rhs);
private :
Bitmap *pb;
};
Widget& Widget::operator= (const Widget& rhs){
Widget temp(rhs);
swap(temp);
return *this;
}
这项技术被称为copy and swap技术。
- 请记住:
- 确保当对象自我赋值时operator=有良好的行为。其中的技术包括
- 比较“来源对象”与“目标对象”的地址
- 精心周到的语句顺序
- Copy and swap技术
- 确定任何函数如果操作一个以上的对象,而其中多个对象是同一个对象时,其行为也是正确的