***************************************转载请注明出处:http://blog.csdn.net/lttree********************************************
二、Constructors,Destructors and Assignment Operators
Rule 11:Handle assignment to self in operator =
规则11:在 operator= 中处理“自我赋值”
1.自我赋值?!
比方这种:
class Widget { ... }; Widget w; ... w = w; // 赋值给自己
2.自我赋值 其它形式
① 除了最直观的 w=w
② 另一些隐蔽的,比方:
a[i] = a[j]; // 万一 i等于j
③ *px = *py
也许。这两个指针都指向同一个对象
④ 还有 base class 与 derived class的:(由于一个基类的引用或者指针。能够指向派生类对象)
class Base { ... }; class Derived : public Base { ... }; void doSomething( const Base& rb , Derived* pd);<span style="white-space:pre"> </span>// rb 和 pd 可能指向同一个对象
3.愚蠢的自我赋值,会导致?
class Bitmap { ... }; class Widget { ... private: Bitmap* pb; }; Widget& Widget::operator=( const Widget& rhs )<span style="white-space:pre"> </span>// 一分不安全的 operator= 实现版本号 { delete pb;<span style="white-space:pre"> </span>// 停止使用当前的 Bitmap pb = new Bitmap(*rhs.pb);<span style="white-space:pre"> </span>// 使用 rhs's的Bitmap 副本 return *this;<span style="white-space:pre"> </span>// 这个问题在上一个条款(条款10)中介绍过 }
就是,假设是
a = b;
工作原理是。先把a的版本号删除。然后在将b赋值给a。
所以,假设a与b 是同一个东西,它就会导致错误,
按上面的样例来讲,就是,删除pb的同一时候。rhs也被删除了,所以第二行的赋值动作就会指向一个已经被删除的对象。
4.看到错误了,总归要解决的
有三种解决方式
<1> 第一种方案——“证同測试” identity test
Widget& Widget::operator=(const Widget& rhs ) { if( this == &rhs ) return *this;<span style="white-space:pre"> </span>// identity test delete pb; pb = new Bitmap(*rhs.pb); return *this; }就是在运行代码前。先推断是否是同一个东西,若是,就不须要做不论什么事情。直接返回。
<2> 另外一种方案——先赋值,再删除
Widget& Widget::operator=(const Widget& rhs) { Bitmap* pOrig = pb;<span style="white-space:pre"> </span>// 先备份 pb = new Bitmap(*rhs.pb);<span style="white-space:pre"> </span>// 赋值 delete pOrig;<span style="white-space:pre"> </span>// 删除备份 return *this; }就是仅仅须要注意在复制之前别删除原来的,先备份一下。
这也许不是处理“自我赋值”最高效的办法。但它行得通。
<3> 第三种方案——copy and swap
class Widget { ... void swap( Widget& rhs );<span style="white-space:pre"> </span>// 交换*this和rhs的数据,在后面条款29会有具体解释 ... }; Widget& Widget::operator=( const Widget& rhs ) { Widget temp(rhs);<span style="white-space:pre"> </span>// 为rhs数据制作一份复件 swap(temp); return *this; }
Widget& Widget::operator= ( Widget rhs )<span style="white-space:pre"> </span>// rhs是被传递对象的一份复件 {<span style="white-space:pre"> </span> swap(rhs);<span style="white-space:pre"> </span>// 注意这里是 pass by value return *this;<span style="white-space:pre"> </span>// 将*this的数据和复件的数据互换 }
但 将 copying 动作 从函数本体 移至 函数參数构造阶段 却能够 让编译器产生更高效的代码。
5.请记住
★ 确保当对象自我赋值时 operator= 有良好行为。当中技术包含比較“来源对象”和“目标对象”的地址、精心周到的语句顺序、以及copy-and-swap。
★ 确定不论什么函数假设操作一个以上的对象,而当中多个对象是同一个对象时,其行为仍然正确。
***************************************转载请注明出处:http://blog.csdn.net/lttree********************************************
原文:http://www.cnblogs.com/mengfanrong/p/5240200.html