“自我赋值”发生在对象被赋值给自己时:
class Widget{};
Widget w;
w=w;//赋值给自己
对于operator=,当类用一个指针指向一块动态分配的内存时,若调用operator=,当不对“自我赋值” 进行处理,可能会产生错误:
class Bitmap{};
class Widget{
public:
Widget& operator=(const Widget& rhs);
private:
Bitmap *pb;
};
//下面是一份不安全的operator实现版本
Widget& Widget::operator=(const Widget& rhs)
{
delete pb;//停止使用当前的Bitmap
pb=new Bitmap(*rhs.pb);//使用rhs的bitmap副本
return *this;
}
存在的自我赋值问题是,operator=函数内的*this(赋值目的端)和rhs可能是同一个对象。若是如此,则delete就不只是销毁当前对象的bitmap,它也销毁了rhs的bitmap。此时,widget中的指针指向了一个已被删除的对象。
解决这个问题的传统方法是在operator=最前面进行“证同测试”:
Widget& Widget::operator=(const Widget& rhs)
{
if(this==&rhs)//当指向同一对象时,不进行赋值
return *this;
delete pb;//停止使用当前的Bitmap
pb=new Bitmap(*rhs.pb);//使用rhs的bitmap副本
return *this;
}
但此版本也存在一个问题,若new Bitmap发生异常(因为分配时内存不足或Bitmap的copy构造函数抛出异常),最终Widget会持有一个指针指向一块已被删除的Bitmap。无法安全地删除这样的指针,也无法安全地读取它们。
另外一个版本,只需注意在复制pb所指东西之前别删除pb:
Widget& Widget::operator=(const Widget& rhs)
{
Bitmap* pOrig=pb;//记住原先的pb
pb=new Bitmap(*rhs.pb);//令pb指向*pb的一个副本
delete pOrig;//删除原先的pb
return *this;
}
对于此版本来说,即使new Bitmap抛出异常,pb也会保持原状,这段代码也能处理自我赋值的问题。
总结
1.确保当对象自我赋值时operator=有良好行为。其中技术包括比较“来源对象”和“目标对象”的地址、精心周到的语句顺序、以及copy-and-swap。
2.确定任何函数如果操作一个以上的对象,而其中多个对象是同一对象时,其行为仍然正确。