在 operator= 中处理自我赋值
自我赋值发生在对象被赋值给自己时,比如w=w,但是更多的是隐性的自我赋值(不能一下子看穿的)
如果i=j,那么a[i]=a[j]算不算自我赋值呢
假设有这么一段代码
class Bitmap {...}
class Widget
{
public:
...
private:
Bitmap* pb;
}
//下面是operator=的代码
Widget& Widget::operator=(const Widget& rhs)
{
delete pb;
pb=new Bitmap(*rhs.pb);
return *this
}
这段operator=的代码没有考虑到this==&rhs
当this==&rhs时,pb早已经被释放了,这样new Bitmap就会因为发现异常而不会生效,其结果是this->pb指向了一个已经删除的对象
解决方法很简单:加个证同测试
Widget& Widget::operator=(const Widget& rhs)
{
if(this==&rhs)
return *this;
delete pb;
pb=new Bitmap(*rhs.pb);
return *this
}
这个方法基本可行,但还是会存在问题,当new操作发生异常时(此时代码就不会往下执行),pb还是会指向一个已经被删除了的对象,这说明这种方法没有”异常安全性”
解决方法是,不考虑证同测试(证同测试会降低代码执行速度),new之后再删除原来的指针
就像下面这样
Widget& Widget::operator=(const Widget& rhs)
{
Bitmap* pOrg=pb;//保存原始的this指针
pb=new Bitmap(*rhs.pb);//将新的对象赋值给this
delete pOrg;//删除原来的指针
return *this
}
这个代码的好处是具备”异常安全性”和”自我赋值安全性”
1.当new操作发生问题时,operator=将会停止往下执行,pb会维持原来的值
2.当this==&rhs时,我们忽略它们是同一个对象的事实,创建一份rhs的副本赋给this,然后删除原始的this对象
当然还有更”骚”的操作:参数传入时byValue,我们知道,byValue时,形参会复制实参的数据,这样就把创建副本的工作交给了构造函数,比如下面这样
Widget& Widget::operator=(const Widget rhs)
{
Bitmap* pOrg=pb;//保存原始的this指针
pb=&rhs;//将新的对象赋值给this
delete pOrg;//删除原来的指针
return *this;
}