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

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

Handle assignment to self in operator=.

什么是自我赋值

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

class Widget { ... };
Widget w;
...
w = w; // 赋值给自己

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

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

// 潜在的自我赋值
class Base { ... };
class Derived : public Base { ... };
void doSomething (const Base* rb, Derived* pd); // rb 和 pd 可能指向同一对象

一份 operator= 错误实现示例

// 假设 Widget 存储了一块动态分配的位图(bitmap)
Widget& Widget::operator=(const Widget& rhs)
{
    delete pb;
    pb = new Bitmap(*rhs.pb);
    return *this;
}
问题一 自我赋值安全性

源和目的可能是同一对象,此时 delete pb 将会销毁当前对象的 bitmap,导致返回后该指针未定义,一种修改方式如下

Widget& Widget::operator=(const Widget& rhs)
{
    if (rhs == *this) return *this; // 证同测试
    delete pb;
    pb = new Bitmap(*rhs.pb);
    return *this;
}
添加“证同测试”使得其具备“自我赋值”安全性,但是仍然存在问题二。
问题二 异常安全性

即使按照问题一的修改方式,仍可能存在问题,如果 new Bitmap 时发生异常,将会导致 pb 失效,一种简单的修改方式如下

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

即使没有“证同测试”,这种修改方式也已经同事解决了问题一。如果比较关心效率,可以在加上“证同测试”,此时需要从效率上衡量必要性
另一种修改方式是 copy and swap 技术

class Widget
{
    ...
    void swap(Widget* rhs);
    ...
};

Widget& Widget::operator=(const Widget& rhs)
{
    Widget temp(rhs); // 为 rhs 制作一份副本
    swap(temp); // 将 *this 与上述副本交换
    return *this;
}

可以使用 pass by value 技巧实现一种巧妙而略失清晰性的方式

Widget& Widget::operator=(Widget rhs) // pass by value
{
    swap(rhs); // 将 *this 与副本交换
    return *this;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值