条款11:在operator= 中处理“自我赋值”
Handle assignment to self in operator=.
异常安全性与自我赋值安全性
接上一篇,当我们让operator= 具备“异常安全性”时,往往会自动获得“自我赋值安全性”的特性。
因此,很多时候,并不专门去解决“自我赋值”的问题,而是将注意力放在“异常安全性(exception safety)”之上。例如对于下面,只需要注意在赋值pb所指的东西之前不要删除pb即可:
Widget& Widget::operator=(const Widget& rhs)
{
Bitmap* pOrig = pb; //记住原先的pb
pb = new Bitmap(*rhs.pb); //令pb指向*pb的一个副本(复件)
delete pOrig; //删除原先的pb
return *this;
}
于是,如果“new Bitmap”抛出异常,pb(及其栖身的那个Widget)也会保持原状。即使没有证同测试,这段代码也是能够处理自我赋值问题:
- 为原bitmap创建一份复件
- 删除原bitmap
- 指向这个新制造的复件
虽然可能这种方法不是处理“自我赋值”最有效的方法,但是确实是一种可行解。
当然,如果我们想要提高效率,也可以将证同测试重新放到函数的起始处。
Copy and Swap
另外,在operator=函数内为了确保代码“异常安全性”与“自我赋值安全性”而手工排列语句的替代方案则是:
- 使用copy and swap技术。
这个技术和“异常安全性”密切相关:
class Widget {
...
void swqp(Widget& rhs); //交换*this和rhs的数据
...
};
Widget& Widget::operator=(const Widget& rhs)
{
Widget temp(rhs); //为rhs数据制作一份复件
swap(temp); //将*this数据和上述的复件进行数据交换
return *this;
}
然而,这种方法的另一种更为高效简洁的写法为:
Widget& Widget::operator=(Widget rhs) //rhs直接就是被传对象的一份复件,此时是pass by value
{
swap(rhs); //将*this数据和上述的复件进行数据交换
return *this;
}
这种方法之所以可以,是因为:
- 某class的copy assignment操作符可能被声明为“以by value方法接受实参”
- 以by value方法传递东西会形成一份复件。
这种方法牺牲了代码的清晰性,但是却将“copy动作”从函数本体内移至“函数参数构造阶段”,使得编译器生成了更有效的代码。
最后: