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

来源:互联网 发布:hdmi网络高清传输器tx 编辑:程序博客网 时间:2024/05/18 01:45

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函数,应该将共同的机能放进第三个函数当中.









阅读全文
0 0