关于赋值,有趣的是你可以把它们写成连锁形式:
int x, y, z;
x = y = z = 15; //赋值连锁形式
为了实现“连锁赋值”,赋值操作符必须返回一个reference指向操作符的左侧实参,这也是你为class实现赋值操作符时应该遵循的协议:
class Widget {
public:
...
Widget& operator=(const Widget& rhs) //返回类型是个reference,指向当前对象
{
...
return* this; //返回左侧对象
}
};
这个协议也适用于所有赋值相关运算符,包括+=、-=*、=等。
在实现赋值操作符有两个很关键的问题需要注意:自我赋值和异常安全性。
假设你建立一个class用来保存一个指针指向一块动态分配到位图:
class Bitmap {...};
class Widget {
...
private:
Bitmap* pb; //指针,指向一个从heap分配而得的对象
};
下面是operator=实现代码,此段代码存在自我赋值和异常安全性问题:
Widget& Widget::operator=(const Widget5& rhs)
{
delete pb; //停止使用当前的bitmap
pb = new Bitmap(*rhs.pb); //使用rhs的bitmap的副本
return *this;
}
自我赋值问题是:函数内的*this(赋值的目的端)和rhs有可能是同一个对象,如果这样则delete就不只是销毁当前对象的bitmap,它也销毁rhs的bitmap,最后会导致自己持有一个指针指向一个已被删除的对象。解决这个问题的方法很简单,只需在operator=函数的最前面做一个“证同测试”以检验“自我赋值”。
异常安全性问题是:如果在new Bitmap时导致异常(不论是因为分配时内存不足或因为Bitmap的copy构造函数抛出异常),Widget最终会持有一个指针指向一块被删除的Bitmap。解决这个问题的方法也很简单,只需要在复制pb所指东西之前别删除pb。
下面是处理自我赋值和异常安全性的operator=实现:
Widget& Widget::operator=(const Widget5& rhs)
{
if (this == &rhs) return *this; //证同测试(identity test)
Bitmap* pOrig = pb; //记住原先的pb
pb = new Bitmap(*rhs.pb); //令pb指向*pb的一个副本
delete pOrig; //删除原先的pb
return *this;
}
阅读(3543) | 评论(1) | 转发(0) |