effective c++阅读之旅---条款11

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

自我赋值案例如下:

class Widget {};
Widget w;
w = w;

这看起来有点愚蠢,但它是合法的,所以不要认定客户绝不会那么做!!

但是,有时,这种自我赋值并不是那么一眼就可以看出来的!

a[i] = a[j] //如果i=j相等显然就是自我赋值!

再看:

*px = *py

如果px和py指向同一个东西,这也是自我赋值。实际上这是由于 “别名” 带来的结果!

一般来说
操作指针指向多个相同类型的对象的情况,这时候需要考虑是否是同一个。

实际上,如果两个对象来自同一个继承体系,它们甚至不需声明为相同类型就可能造成"别名", 因为一个基类的指针或引用可以指向一个派生类的对象。

如下:

class Base {};
class Derived : public Base{};
//这里很显然base和pb有可能其实是同一个对象
void DoSomething(const Base& base, Derived* pb);

下面讲一下用于资源管理类的一个赋值重载的常见操作:

class Bitmap{}; //资源类
class Widget//含有资源的类
 {
public:
    Widget& operator=(const Widget& rhs)
    {
        if (this == &rhs)return *this; //自我赋值检测,自我赋值直接返回

        delete pb;
        pb = new Bitmap(*rhs.pb);
        return *this;
    }
private:
    Bitmap* pb;
};

上述的写法几乎是最常见的写法,虽然可以达到防止自我赋值的问题
但是不具备"异常安全性",如果delete pb; 已经被执行,但是 pb = new Bitmap(*rhs.pb); 执行失败了,这时候的Widget对象的pb指向未知!

但是我们精心简单的修改一下,它就可以满足我们的异常安全性和防止自我赋值:

    Widget& operator=(const Widget& rhs)
    {
        if (this == &rhs)return *this; //自我赋值检测,自我赋值直接返回
        
        Bitmap* ptemp = pb;  //先用指针记录一下资源
        pb = new Bitmap(*rhs.pb); //然后new抛出异常,则原本的pb还是保持不变!
        delete ptemp;
        return *this;
    }

同样有另一种更完美的方案可代替上述方案:copy and swap技术

 void Swap(Widget& rhs){} //函数内完成*this 和 rhs的交换数据
 Widget& operator=(const Widget& rhs)
 {
     Widget temp(rhs); //为rhs数据制作一个复件
     Swap(temp);         //将*this数据和上述复件的数据交换!
     return *this;
 }

总结:

确保对象自我赋值=有良好行为。包括:来源对象和目标对象地址不是同一个,精心周到的语句顺序、copy-and-swap技术

结尾: 我是航行的小土豆,喜欢我的程序猿朋友们,欢迎点赞+关注哦!希望大家多多支持我哦!有相关不懂问题,可以留言一起探讨哦!

如有引用或转载记得标注哦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值