Effective C++ — 构造/析构/赋值运算(二)

Effective C++ 

———————————————————————


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


有的人可能想了,有谁会写出 a = a;这种表达式这个条款是拿来充数的吧? 你还真的别这么说,这种情况还真的有情

况发生.比如有 的自我赋值你根本看不出来:比如:

a[i] = a[j]; //潜在的自我赋值,如果i = j的时候.

*px = *py;  // 潜在的自我赋值,如果px和py恰巧指向同一个东西.

这些都不是明显的自我赋值,是"别名"带来的结果.一般而言当某段代码操作Points和reference而他们被用来"指向多

个相同类型的对 象",就需要考虑这些对象是否为同一个.实际上两个对象只要来自同一个继承体系,他们甚至不许声明

为相同类型就可能造成"别名" 因为一个base class的reference或者points可以指向derived class对象.

我们一般的operator=实现代码为:

widget& operator=(const widget& rhs)
{
	delete pb;  //假设pb为管理资源的指针
	pb = new Bitma(*rhs.pb);
	return *this;
}

但是如果不慎出现别名,那么你是先删除掉自己然后再自己对自己赋值? 让自己的指针指向一个被销毁的地方? 所以

我们需要加 上一层判断.

widget& operator=(const widget& rhs)
{
	if (this == &rhs)
	{
		return *this;
	}
	delete pb;  //假设pb为管理资源的指针
	pb = new Bitma(*rhs.pb);
	return *this;
}

这样做是一点问题都没有的,但是这个新版本会存在异常方面的麻烦.更明确的说,如果"new Bitma"导致异常,widget最终会持有

一个指针指向一块被删除的bitmap.这样的指针是有害的,你无法安全的删除它们,甚至无法安全的读取他们.唯一能对他们做的安

全事件是付出许多调试能力找出错误根源.

如果你为了让operator= 具有"异常安全性,所以我们只需要注意在复制pb所指东西之前别删除pb:

widget& operator=(const widget& rhs)
{
	Bitma* porig = pb; //假设pb为管理资源的指针
	pb = new Bitma(*rhs.pb);
	delete porig;  
	return *this;
}

现在,如果"new Bitma"抛出异常,pb保持原状.即使没有证同测试,这段代码还是能够处理自我赋值,因为我们对原pb做了一件

复件,然后重新定义pb,最后pb定义好了之后,删除掉复件. 它或许不是处理"自我赋值"的最高效方法,但它行得通.

总结:

确保当对象自我赋值时operator有良好的行为,检查他是否存在自赋值.

确定任何函数如果操作一个或一个以上的对象,而其中多个对象是同一个对象时,其行为仍然正确.



条款12:复制对象时勿忘其每一个成分



设计良好之面向对象系统会将对象的内部封装起来,只留下两个函数负责对象拷贝,那就是copy构造函数和

copy assignment操作符 我称他们为copying函数.


如果你声明自己的copying函数,意思就是告诉编辑器你并不喜欢却省实现中的某些行为。编译器就会觉得被冒犯一

样,会用一种奇 怪的方式来回敬你,当你的代码出现一点错误的时候却不告诉你.


我就这样说吧,当你编写一个copying函数请确保两件事情:

1.复制所有的local成员变量.

2.调用所有base classes内的适当的copying函数.


其实两个copying函数往往拥有相同的实现本体,这可能会引诱你让某个函数去乔勇另一个函数以避免代码重复.这样

精益求精的态 度值得赞赏,但是令某个copying函数调用一个copying函数却无法让你达到你想要的目标.


令copy assignment操作符调用copy构造函数是不合理的,因为这就像试图构造一个已经存在的对象.这挺起来就很不

通顺.单纯的接 受这个建议:你不该令copy assignment操作符调用copy构造函数.


令copy构造函数调用copy assignment操作符同样没有意义.构造函数是用来初始化新对象,而assignent操作符知识

性于已初始化对 象身上.对应尚未构造好的对象赋值,就像在一个尚未初始化的对象身上做"只对已初始化对象才有意

义"的事情一样.同样无聊. 如果你发现你的copy构造函数和 copy assignment操作符有相近的代码,消除重复代码的做

法就是建立一个新的成员函数给两者调用 这样的函数往往是private而且常被命名为init. 你可以尝试这样.


总结:


Copying函数应该确保复制"对象内的所有成员变量"及"所有base class 成分"

不要尝试以某个copying函数实现另一个copying函数,应该将共同的机能放进第三个函数当中.









你可能感兴趣的:(Effective,C++,读后积累)