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

127 篇文章 7 订阅
39 篇文章 3 订阅

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

Handle assignment to self in operator=.
本章较长,分为两部分。

自我赋值

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

class Widget { ... };

Widget w;
...
w = w;          //赋值给自己

虽然这种做法看起来比较傻,但是这种操作却是合法的。
此外,自我赋值并不是总是可以一眼分辨出来,例如:

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

如果i和j具有相同的值时,这就是一个自我赋值。再比如:

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

如果指针px和py恰巧指向同一个东西,这也是一个自我赋值。

这些并不明显的复制行为,是“别名(aliasing)”所带来的结果。所谓“别名”:

  • 别名就是有一个以上的方法指称(指涉)某对象。

一般而言,如果某段代码操作pointers或references而它们被从来“指向多个相同类型的对象”,就需要去考虑这些对象是否为同一个对象。
实际上,两个对象只要来自同一个继承体系,它们甚至不需要声明为相同类型就可能会造成“别名”,因为一个base class的reference或者pointer可以指向一个derived class对象:

class Base { ... };
class Derived : public Base { ... };
void doSomethings(const Base& rb, Derived* pd);   //rd和*pd有可能其实是同一个对象

在这里,假如说我们尝试自行管理资源(即打算写一个用于资源管理的class,就需要这样做),就可能会掉进“在停止使用资源之前意外释放了它”的陷阱。举个例子,假如建立一个class用来保存一个指针指向一块动态分配的位图(bitmap):

class Bitmap { ... };
class Widget {
    ...
private:
    Bitmap* pb;     //指针,指向一个从heap分配而得的对象
};

接着,下面的operator= 的实现代码,看起来虽然合理,但是在进行自我赋值时并不安全

Widget& Widget::operator=(const Widget& rhs)    //不安全的operator= 的实现版本
{
    delete pb;     //停止使用当前的bitmap
    pb = new Bitmap(*rhs.pb);       //使用rhs's bitmap的副本(复件)
    return *this;
}

之所以会出现自我赋值的问题,operator= 函数内的*this(赋值的目的端)和rhs有可能是同一个对象。如果它们是同一个对象,那么delete对象就不只是销毁当前对象的bitmap,它也同时销毁了rhs的bitmap。
因此,在函数末尾,对于Widget,它原本不应该被自我赋值动作改变的,然而此时:

  • Widget发现自己持有一个指针,指向一个已经被删除的对象。

想要阻止这样的错误,传统的做法是:

  • operator= 最前面进行一个“证同测试(identity test)”,以此达到自我赋值的检验目的
Widget& Widget::operator=(const Widget& rhs)
{
    if(this == &rhs)        //证同测试(identity test)
        return *this;       //如果是自我赋值,就不做任何事情
    delete pb;
    pb = new Bitmap(*rhs.pb);
    return *this;
}

这种解决办法是行得通的。在第一个版本的operator= 中,不仅不具备“自我赋值安全性”,也不具备“异常安全性”,然而,这个新版本的operator=,仍然存在异常方面的问题
如果“new BItmap”导致了异常(比如因为分配时内存不足或者因为Bitmap的copy构造函数抛出异常),Widget最终会持有一个指针指向一个被删除的Bitmap。这样的指针是有害的:

  • 既不能安全的删除它,也不能安全的读取它。

唯一能对它们做的安全的事情就是付出很多调试的功夫,去找到错误的起源。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值