首页 > 代码库 > Effective C++ Item 11 在operator= 中处理“自我赋值”

Effective C++ Item 11 在operator= 中处理“自我赋值”

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie



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

示例:没有“证同测试”

#include <iostream>
#include <string>
using namespace std;

class Bitmap{
public:
	Bitmap(int s):size(s){}
	Bitmap(const Bitmap &b){
		this->size = b.size;
	}
	int getSize() const{return size;}
private:
	int size;
};

class Widget{
public:
	Widget(int size){
		pb = new Bitmap(size);
	}
	Widget &Widget::operator=(const Widget &rhs){
		delete pb;			//停止使用当前的bitmap
		pb = new Bitmap(*rhs.pb);	//使用rhs's bitmap的副本
		return *this;
	}	
	Bitmap *getBitMap() const{return pb;}
private:
	Bitmap *pb;			//指针,指向一个从heap分配而得的对象
};


int main(){
	Widget w(1);
	cout << "before self assignment: " << w.getBitMap()->getSize() << endl;
	w = w;
	cout << " after self assignment: " << w.getBitMap()->getSize() << endl;
	system("pause");
}

输出:

before self assignment: 1

after self assignment: -842150451


解析:

operator=函数内的*this和rhs是同一个对象,delete销毁当前对象的bitmap,也销毁了rhs的bitmap,在函数末尾,Widget用自己持有一个指向已被删除的对象指针创建新的bitmap。所以当自我赋值后,w对象里的值变成了乱码。

纠正1:通过“证同测试”来避免自我赋值

示例:加了“证同测试”

#include <iostream>
#include <string>
using namespace std;

class Bitmap{
public:
	Bitmap(int s):size(s){}
	Bitmap(const Bitmap &b){
		this->size = b.size;
	}
	int getSize() const{return size;}
private:
	int size;
};

class Widget{
public:
	Widget(int size){
		pb = new Bitmap(size);
	}
	Widget &Widget::operator=(const Widget &rhs){
		if(this == &rhs) return *this; //这里加了“证同测试”
		delete pb;//停止使用当前的bitmap
		pb = new Bitmap(*rhs.pb);	//使用rhs's bitmap的副本
		return *this;
	}	
	Bitmap *getBitMap() const{return pb;}
private:
	Bitmap *pb;			//指针,指向一个从heap分配而得的对象
};


int main(){
	Widget w(1);
	cout << "before self assignment: " << w.getBitMap()->getSize() << endl;
	w = w;
	cout << " after self assignment: " << w.getBitMap()->getSize() << endl;
	system("pause");
}

输出:

before selfassignment: 1

after self assignment: 1


纠正2:通过处理“异常安全性”来自动获得“自我赋值安全”的回报

#include <iostream>
#include <string>
using namespace std;

class Bitmap{
public:
	Bitmap(int s):size(s){}
	Bitmap(const Bitmap &b){
		this->size = b.size;
	}
	int getSize() const{return size;}
private:
	int size;
};

class Widget{
public:
	Widget(int size){
		pb = new Bitmap(size);
	}
	Widget &Widget::operator=(const Widget &rhs){
		Bitmap* pOrig = pb;         //记住原先的pb
		pb = new Bitmap(*rhs.pb);	//使用rhs's bitmap的副本
		delete pOrig;				//删除原先的pb
		return *this;
	}	
	Bitmap *getBitMap() const{return pb;}
private:
	Bitmap *pb;			//指针,指向一个从heap分配而得的对象
};


int main(){
	Widget w(1);
	cout << "before self assignment: " << w.getBitMap()->getSize() << endl;
	w = w;
	cout << " after self assignment: " << w.getBitMap()->getSize() << endl;
	system("pause");
}

输出:

before selfassignment: 1

after self assignment: 1  


解析:如果“newBitmap”抛出异常,pb保持原状;这段代码也能处理自我赋值。还可以把“证同测试”再次放回函数起始处,双重保险,不过有其他执行速度等的代价


纠正3:copy and swap技术

示例:

<pre name="code" class="cpp">	Widget &Widget::operator=(const Widget &rhs){
		Widget temp(rhs);
		swap(temp);
		return *this;
	}
	或
	Widget &Widget::operator=( Widget rhs){ //将”copying动作”从函数本体移到“函数参数构造阶段” 
		swap(rhs);
		return *this;
	}