Effective C++ 条款11:在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有可能其实是同一对象

尝试自我管理资源

class Bitmap { ... };
class Widget {
	...
private:
	Bitmap* pb;			// 指针,指向一个从heap分配而得的对象
};
// 一个不安全的operator=实现版本
Widget& Widget::operator=(const Widget& rhs) {
	delete pb;						// 停止使用当前的bitmap
	pb = new Bitmap(*rhs.pb);		// 使用rhs's bitmap的副本
	return *this;					// 见条款10
}

可能出现的问题就是,*this和rhs可能是同一个对象,最后会导致自己持有一个指针指向一个已被删除的对象!

自我安全性

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

以上代码确实解决了自我赋值的问题,但是会引发异常的问题,如果new Bitmap导致异常了,那么Widget最终会持有一个指针指向一块被删除的Bitmap。

异常安全性

Widget& Widget::operator=(const Widget& rhs) {
	Bitmap* pOrig = pb;						// 记住原先的pb
	pb = new Bitmap(*rhs.pb);
	delete pOrig;							// 删除原先的pb
	return *this;
}

copy and swap

class Widget {
...
void swap(Widget& rhs);
...
};
Widget& Widget::operator=(const Widget& rhs) {
	Widget temp(rhs);
	swap(temp);
	return *this;
}
Widget& Widget::operator=(Widget rhs) {	// 注意这里是传值
	swap(rhs);					
	return *this;
}

总结

  1. 确保当对象自我赋值时operator=有良好行为。其中技术包括比较”来源对象”和”目标对象”的地址、精心周到的语句顺序、以及copy-and-swap.
  2. 确定任何函数如果操作一个以上的对象,而其中多个对象是同一个对象时,其行为仍然正确。
posted @ 2017-08-28 14:43  清水汪汪  阅读(462)  评论(0编辑  收藏  举报