条款11:在operator中处理“自我赋值”

自我赋值出现在对象自己给自己赋值的过程中。
有时候容易察觉,有时候不容易察觉。特别是当在有继承关系的类对象中,一个基类的引用或指针可以指向子类的对象。

class Book{};
class Store{
private:
Book *pb;//指向一个从heap分配的对象
}
Store & Store::operator = (const Store & st)
{
   delete pb;
   pb = new Stroe(*st.pb);
   return *this;
}


上面的这个程序有问题,不仅没有(self-assignment-safe)自我赋值安全性而且也没有(exception safety)异常安全性。
因为st跟*this可能是同一个对象,所以当delete pb的时候也delete掉了st中的Book对象。最后return的时候,发现这个st中的指针指向了一个被删除的对象。
防止这种的方法有进行检验是否是同一个对象。

Store & Store::operator = (const Store & st)
{
   if(this==&st)
      return *this;//如果是同一个对象,那么直接返回这个对象就可以
   delete pb;
   pb = new Stroe(*st.pb);
   return *this;
}

然后一个问题是异常安全性
在new操作的时候可能会抛出异常,但此时pb已经被删除掉,造成了这个对象的执政指向了一个被删除的对象。
改进的方法如下:

Store & Store::operator = (const Store & st)
{
   Book * bt = pb;
   pb = new Stroe(*st.pb);
   delete bt;
   return *this;
}

这样如果new抛出异常,那么pb还将保持原有的指针。
更高级一点的技术是:
用手工的方法安排赋值语句的顺序

Stroe{
   void swap(Store &st);//这个函数的作用是交换*this 与st中的数据
}
Store & Store::operator = (const Store & st)
{
   Stroe stemp(st);
   swap(stemp);
   return *this;
}
//如果采用的是传值可以用下面的代码来实现
Store & Store::operator = ( Store  st)
{
   swap(stemp);//传值时形参会有一个临时对象,所有不必像上面一样创建临时对象了
   return *this;
}

通过上面的操作,实现了 operator=操作符的安全性。

4 thoughts on “条款11:在operator中处理“自我赋值””

发表评论

电子邮件地址不会被公开。 必填项已用*标注