条款 11 :在operator= 中处理“自我赋值”
**Handle assignment to self in operator= **
- 自我复制发生在对象被赋值给 自己时,这听起来很荒谬但是请注意,这是合法的。
class Widget{...};
Widget w;
...
w=w//易于识别的自我赋值
//潜在的自我赋值
a[i]=a[j]//如果i=j
//潜在的自我赋值
*px=*py;//如若px,py指向同一个对象
- 这些不明显的自我赋值往往是“别名”带来的结果。所谓别名就是有一个以上的方法指称(涉及)某对象。
//一般而言指针和引用会带来上述问题
//但实际上只要两个对象来自一个继承体系
//甚至不需要声明为相同类型,就可能造成“别名”
class Base{...};
class Drived:public Base{...};
//rd和pd有可能其实是同一对象。
void doSomething(const Base& rb,Drived* pd);
- 注意自我赋值的安全使用,防止“在停止使用资源之前就意外释放了他”的陷阱。
//假设你建立一个类,用来保存一个指针指向一块动态分配的位图
class Bitmap{...};
class Widget{
...
private:
Bitmap* pd;
};
//下面是operator=实现代码
//表面上看起来安全,但是在自我赋值出现时并不安全。
//一份不安全的operator=实现版本
Widget& Widget::operator=(const Widget& rhs){
delete pd;//停止使用当前的bitmap
pd = new Bitmap(*rhs.pb);//使用rhs's bitmap的副本
return *this;//见条款10
}
//这里的自我赋值问题是operator=两边可能是同一个对象。
//若真是如此,该函数返回了一个指针指向了一个不存在的对象
//预防止这种错误,传统方法是“证同测试”
//达到是否是“自我赋值”的检验目的
Widget& Widget::operator=(const Widget& rhs){
//如果是“自我赋值”就什么也不做
if(this == &rhs)return *this;//证同测试
delete pd;
pd = new Bitmap(*rhs.pb);
return *this;//见条款10
}
//这么做行得通
//但是如果Bitmap分配内存时出问题,依然会导致上述问题
//即Widget拥有一个野指针,而你却无法处理这个野指针
- 许多时候一些精心安排的语句就可以导出异常安全(以及自我赋值安全)的代码。
Widget& Widget::operator=(const Widget& rhs){
Bitmap *pOrig=pd;//记住原先的pd
pd = new Bitmap(*rhs.pb);//令pd指向*pd的副本
delete pOrig;//删除原先的pd
return *this;//见条款10
}
//现在即使new Bitmap时抛出异常,
//pd以及他栖身的Widget会保持原状,因为我们有备份
//该方法或许不是效率最高的方法但是行得通
//copy and swap 技术
class Widget{
...
//交换*this与rhs的数据
void swap(Widget& rhs);
...
};
Widget& Widget::operator=(const Widget& rhs){
Widget tmp(rhs);//为rhs制作一份副本
swap(temp);//将*this与上述副本的数据交换
return *this;
}
请记住
1. 确保当对象自我赋值时operator=有良好的行为。其中技术包括比较“来源对象和目标对象”的地址,精心周到的语句顺序,以及copy and swap 技术。
2. 确定任何函数如果操作一个 以上的对象,其中多个对象如果是一个对象的时,其行为仍然正确。