C++中在operator=中处理“自我赋值”(11)---《Effective C++》

来源:互联网 发布:淘宝标题营销词 编辑:程序博客网 时间:2024/05/17 22:33

条款11:在**重点内容**operator=中处理“自我赋值”

“自我赋值”发生在对象被赋值给自己时:
class Widget{…}
Widget w;

w=w;//赋值给自己
a[i]=a[j];//潜在的自我赋值,如果i和j有相同的值,这便是个自我赋值
*px=*py;//潜在的自我赋值,如果px和py指向同一个问题,这也是自我赋值
甚至如下这样也可以:
class Base {…};
class Derived:public Base {…};
void doSomething(const Base& rb,Derived* pd);//rb和pd可能其实是同一个对象

很多情况下“自我赋值”是安全的,不需要额外操心,然而如果你尝试自行管理资源(如果你打算写一个用于资源管理的class就这样做),可能会掉进“在停止使用资源之前以外释放了它”的陷阱,如:

class Bitmap {...};class Widget{    ...private:    Bitmap* pb;};Widget& Widget::operator=(const Widget& rhs){    delete pb;    pb=new Bitmap(*rhs.pb);    return *this;}

这里的自我赋值问题是,operator=函数内部*this和rhs有可能是同一个对象,如果这样的话,delete pb销毁的不只是当前对象那个的bitmap,同时rhs的bitmap也被销毁掉了,那么在自我赋值的时候程序会发现rhs.pb指针指向的是一个已经被删除的对象!

如何来阻止这种错误呢,具体有多种方法:
1)传统做法是operator=最前面加一个“证同测试”达到“自我赋值”的校验目的,具体见如下代码:

Widget& Widget::operator=(const Widget& rhs){    if(this==&rhs) return *this;//证同测试    delete pb;    pb=new Bitmap(*rhs.pb);    return *this;}

这样的代码有没有什么错误呢?可以看到,具备了“自我赋值安全性”,那还有什么问题呢?答案就是“异常安全性”,这种解决方法对于异常并没有什么很好的作用,具体来说,如果new Bitmap(*rhs.pb)出现异常的话(不论是因为分配时内存不足或者Bitmap的copy构造函数抛出异常),pb最终指向一个被删除的Bitmap,这样的指针会出现空指针异常等乙烯类问题。
2)这种方法主要侧重于“异常安全性”方面,我们只需注意在赋值pb所指的东西之前别删除pb:

Widget& Widget::operator=(const Widget& rhs){    Bitmap* pOrig=pb;    pb=new Bitmap(*rhs.pb);    delete pOrig;//删除原先的pb    return *thsis;}

这样的话,如果“new Bitmap”抛出异常,pb保持原状,即使没有“证同测试”,这段代码还是可以处理自我赋值,因为我们对原bitmap做了一份复件、删除原bitmap、然后指向新制造的那个复件。PS:如果此时需要考虑效率的话,需要先估计“自我赋值”发生的频率有多高?如果高的话,我们就将“证同测试”加在函数起始处?如果“证同测试”发生频率不高的话,推荐不要添加“证同测试”,因为添加这项测试也需要耗费成本,降低程序执行效率。

3)如果对上述两种方法还是不太满意的话,还有一个替代方案,不仅“异常安全”同时“自我赋值安全”,即使用copy and swap技术实现,参看以下代码:

class Widget{    ...    void swap(Widget& rhs);    ...};Widget& Widget::operator=(const Widget& rhs){    Widget temp(rhs);//使用copy构造函数,为rhs数据制作一份副本    swap(temp); //将*this数据和上述所述复件的数据交换    return *this;}   

怎样,这种方法不错吧!但是代码看起来逻辑并没有多么清晰,这也是这种方法的缺点吧!

总结:
1)确保当前对象自我复制时operator=有良好的行为,其中包括“来源对象”和“目标对象”的地址、精心周到的语句顺序以及copy-and-swap操作;
2)确定任何函数如果操作一个以上的对象,而其中多个对象是同一个对象那个时候,其行为依然正确。

阅读全文
0 0