Item11:在operator=中处理自我赋值

0.概述

  • 确保当对象自我赋值时operator=有良好行为。包括比较“来源对象”和“目标对象”的地址、精心周到的语句顺序、以及copy-and-swap.
  • 确定任何函数如果操作一个以上的对象,而其中多个对象是同一个对象时,其行为仍然正确。

1.自我赋值

发生在对象被赋值给自己

1.1 显而易见的

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

1.2 潜在自我赋值

如果i和j值相同,就是自我赋值:

a[i] = a[j];

如果px和py恰巧指向同样的东西,也是自我赋值:

*px = *py;

这些潜在自我赋值,是“别名”(aliasing)带来的结果:所谓“别名”就是“有一个以上的方法指称(指涉)某对象”。

一般而言如果某段代码操作pointers 或references用来“指向多个相同类型的对象”,就需考虑这些对象是否为同一个。

两个对象只要来自同一个继承体系,它们甚至不需声明为相同类型就可能造成“别名”,因为一个base class的 reference或pointer可以指向derived class对象:

class Base { ... };
class Derived: public Base { ... };
void doSomething(const Base& rb, // rb and *pd可能指向
Derived* pd); // 同一对象

2.自我赋值带来的问题

可能会在停止使用资源之前意外释放了它,这样将会导致指针指向一个被删除的对象

3.解决方案

3.1 证同测试

Widget& Widget::operator=(const Widget& rhs)
{
    if (this == &rhs) return *this;//证同测试,如果是自我赋值,不做任何事
    delete pb;
    pb = new Bitmap(*rhs.pb);
    return *this;
}

3.2 异常安全性

如下代码,只需在复制pb所指东西前别删除pb:

Widget& Widget::operator=(const Widget& rhs)
{
    Bitmap *pOrig = pb; //保存原先的pb
    pb = new Bitmap(*rhs.pb); //令pb指向*pb的副本
    delete pOrig; //删除原先的pb
    return *this;
}

这段代码在具备异常安全性的同时也自动获得了“自我赋值安全”,而不需做证同测试(因为我们对原bitmap做了一份复件、删除原bitmap、然后指向新制造的那个复件)。

这或许不是处理“自我赋值”的最高效办法,但行得通。
如果你很关心效率,可以把“证同测试”( identity test)再次放回函数起始处。但这样做会影响程序效率:

  • 代码变大
  • 导入新的控制流

3.3 copy and swap

确保代码不但异常安全而且自我赋值安全。

class Widget {
    ...
    void swap(Widget& rhs); // exchange *this’s and rhs’s data;
    ... // see Item29 for details
};
Widget& Widget::operator=(const Widget& rhs)
{
    Widget temp(rhs); // make a copy of rhs’s data
    swap(temp); // swap *this’s data with the copy’s
    return *this;
}

这种实现基于以下两个事实:

  • 某 class 的 copy assignment操作符可能被声明为“以by value方式接受实参”
  • 以 by value方式传递东西会造成一份复件/副本
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值