Effective C++中条款10和条款21的理解
一个多月前把Effective C++看完了,后来就是期末考试,一直没有时间好好的整理一下,今天再翻一遍发现好多知识点和笔记都不懂了,果然不能再拖了,应该学完就汇总成笔记的。
首先回顾一下条款10和条款21的内容吧。
一、条款10 令operator=返回一个reference to *this
书上的代码如下:
class Widget{
public:
...
Widget& operator=(const Widget& rhs){
...
return *this;
}
...
};
几个常见的疑问解答:
参考链接
①operator=的参数为什么要求const和&?
A:赋值运算符重载函数的参数是函数所在类的const类型的引用,加上const是为了不修改原值,加上&是为了直接使用实参,避免拷贝提高效率。
②为什么赋值操作需要返回一个reference指向左侧实参?
A:在代码中,this是调用该函数(在此处即为operator=)的对象的地址,*this即代表当前的对象。这里的问题其实可以分解成两个,第一个问题是为什么需要有返回值,赋值操作符没有返回值行不行?第二个问题是返回值为什么需要是引用,返回值类型行不行?
对于第一个问题,答案是为了实现连续赋值。例如a=b=c;这样的语句,如果是void没有返回值,那么处理完b.operator=(c)之后,没有返回值给a.operator=(),这样就实现不了连续赋值了。
对于第二个问题,答案是为了应对(a=b)=c;这种情况,这里打破了从右往左赋值的顺序,首先运算a=b,由于返回的是一个值类型而不是一个引用,编译器会对返回的对象进行一次拷贝,得到一个临时的副本,按C++规定来说这个副本是一个右值,接下来进行=c的操作,副本作为右值出现在等号左边,自然是不行的。返回引用也有个效果,
总的来说问题①②只在特定问题下出问题,并不是强制要求解决的,但是为了保证重载后的运算符尽量保留原特性,最好还是这么做。原赋值运算符=能做到a=b=c和(a=b)=c,不能重载后就做不到对不对?
二、条款21 必须返回对象时,别妄想返回其reference
书上代码如下:
const Rational& operator*(const Rational lhs, const Rational& rhs)
{
Rational result(lhs.n*rhs.n, lhs.d*rhs.d);
return result;
}
这里重载了乘法操作符,用于进行有理数的乘法,分子分母分别相乘。此处在stack空间创建了一个local对象(局部对象)result,为了避免调用构造函数,函数返回一个引用指向result,而不是值类型。
可以容易的看出这个函数存在的题,因为函数内部定义的变量result是个local变量,而local对象在函数退出前被销毁了,因此返回的引用是是一个“残骸”,这很明显会造成问题。事情的真相是,任何函数如果返回一个reference指向某个local对象,都将一败涂地。
实际上条款21不仅探讨了on the stack(栈区)的情况,也讨论的on the heap的情况,和本文关系不大就不赘述了。
三、条款10与21的不同点
第一次阅读条款21的时候,马上就翻回条款10,因为同样是运算符重载,为什么operator=最好是返回引用,而operator*则不返回?
分析了一下还是因为赋值操作符的特殊性,operator=重载只能是成员函数,而且函数体中有return *this,即当前调用函数的对象已经存在,而不是临时构建的local对象,自然不会出现第二部分的情况。
四、总结
使用引用作为返回值可以减少开销,实现连续赋值,但是也并不是什么情况下都能使用引用的,那么问题来了,究竟什么时候使用引用?已经有大神为我们总结好了:
C++中引用(&)的用法和应用实例
摘录一段:
流操作符<<和>>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数需要使用引用,
在另外的一些操作符中,却千万不能返回引用:+-*/ 四则运算符。它们不能返回引用
再啰嗦一下,其中拷贝构造函数的参数必须是引用,而不是“推荐使用引用”,至于原因可以参考拷贝构造函数的参数类型必须是引用。
总结下来是并不能无脑使用reference来提高效率,很多地方需要思考一下,就像条款21末尾作者总结的那样:当你必须在“返回一个reference和返回一个object”之间抉择时,你的工作就是挑出行为正确的那个。