11.Handle assignment to self in operator =

在operator = 中处理“自我赋值”“自我赋值”时常很容易被修正。

但下面的情况:

a[i] = a[j]; 
*px = *py;

如果i = j, px和py都指向同一个对象,就会出现“自我赋值”。

有如下类:

class Bitmap { ... };
class Widget 
{
public:
	Widget& operator = (const Widget& rhs); 
...

private:
	Bitmap* pb; // 指向堆内存 
}

对于如下operator = 实现:

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

看起来合情合理,但是存在问题。
如果出现“自我赋值”,即 *this等于rhs, delete pb等同于delete rhs.pb。

可以加上“证同测试”:

Widget& Widget::operator = (const Widget& rhs)
{
	if (this == &rhs) return *this; 

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

这样就具备了“自我赋值安全”,但任存在“异常安全性”问题。
如果new Bitmap导致异常,目标对象将持有一个已被销毁的pb指针。

令人高兴的是,让operator = 具备“异常安全性”往往自动获得“自我赋值安全”的回报。

一种合适办法是:

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

如果Bitmap抛出异常,由于pb还未delete,因此不会有问题。
即使没有“证同测试”, 依然没有问题, 因为使用了pOrig保存了pb。

还有一种叫做copy and swap的技术:

class Widget
{
public:
	void Swap(Widget& rhs); // 交换*this和rhs的数据
...
}

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

其实,这段代码还有优化空间,请移步:copy-and-swap idiom详解和实现安全自我赋值

请记住:

  • 确保当对象自我赋值时operator = 有良好行为。其中技术包括比较“来元对象”和“目标对象”的地址、精心周到的语句顺序、以及copy-and-swap。

  • 确保任何函数如果操作一个以上的对象,而其中多个对象时同一个对象时, 其行为依然正确。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值