“自我赋值”发生在对象被赋值给自己:

  1. class Widget{...}; 
  2. Widget w; 
  3. ... 
  4. w =w;//赋值给自己 

看起来有点蠢,但它合法。赋值动作并不总是那么可被一眼看出来,例如:

a[i] = a[j];//潜在的自我赋值

*px = *py;//潜在的自我赋值

这些并不明显的自我赋值,是“别名”带来的结果:所谓“别名”就是“有一个以上的方法指称某对象”。

如果你尝试自行管理资源,可能会掉进“在停止使用资源之前意外释放了它”的陷阱。假设你建立一个类用来保存一个指针指向一块动态分配的位图:

  1. class Bitmap{...}; 
  2. class Widget 
  3.    ... 
  4. private
  5.   Bitmap *pb;//指针,指向一个从heap分配而的的对象 
  6. }; 
  7. //下面是operator=代码的实现。表明上看起来合理, 
  8. //但自我赋值出现时并不安全 
  9. Widget& Widget::operator=(const Widget& rhs)//一份不安全的operator=实现 
  10.   delete pb;//停止使用当前的bitmap, 
  11.   pb = new Bitmap(*rhs.pb);//使用rhs 的 bitmap副本 
  12.   return *this

 这里的自我赋值问题是,operator=函数内的*this和rhs有可能是同一个对象。如果是同一个对象,那delete就不只是销毁当前对象的bitmap,它也销毁rhs的比bitmap。在函数末尾,Widget--它原本不该被自我赋值动作改变的--发现自己持有一个指针指向一个已删除的对象!

欲阻止这种错误,传统做法是藉由operator=最前面的一个“证同测试”达到“自我赋值”的检查目的:

  1. Widget& Widget::operator=(const Widget& rhs) 
  2.   if(this==&rhs) return *this
  3.    
  4.   delete pb; 
  5.   pb = new Bitmap(*rhs.pb); 
  6.   return *this

这个版本仍然存在异常方面的麻烦,如果new Bitmap导致异常(不论是因为分配内存不足或因为Bitmap的copy构造函数抛出异常),Widget最终会持有一个指针指向一块被删除的Bitmap,这样的指针有害。你无法删除它们,无法读取它们,唯一能对它做的安全的事情是付出许多调试找出错误起源。

“许多时候一群精心安排的语句就可以到处异常安全的代码”

以下代码,我们只需要注意在赋值旁边所指东西之前别删除pb:

  1. Widget& Widget::operator=(const Widget& rhs) 
  2.   Bitmap * pOrig = pb; //记住原先的pb 
  3.   pb = new Bitmap(*rhs.pb);//令pb指向*pb的一个副本 
  4.   delete pOrig;//删除原先的pb 
  5.   return *this

 现在如果new Bitmap抛出异常,pb保持原状,即使没有证同测试,这段代码还是能够处理子午赋值,因为我们对原bitmap做了一份副本,删除原bitmap,然后指向新制造的那个副本。它或许不是处理自我赋值的最高效办法,但它行得通。

 

copy-and-swap

 

...

 

 

1.确保当对象自我赋值时operator=有良好行为。其中包括比较“来源对象”和“目标对象”的地址,精心周到的语句顺序,以及copy-and-swap。

2.确定任何函数如果操作一个以上的对象,而其中多个对象是同一个对象时,其行为仍然正确。