[Effective C++ --021]必须返回对象时,别妄想返回其reference
引言
在条目20中,我们知道了值传递和引用传递的效率问题,因此在设计程序时,我们可能就尽可能来返回引用而不是值。
可是,可能会犯下面的一些错误:传递一些引用指向其实并不存在的对象。
第一节:返回临时变量的引用
假如我们有以下的例子,先看值传递
1 class A { 2 public: 3 A(int n = 0, int d = 1):n(n),d(d) {} 4 private: 5 int n,d; 6 friend const A operator* (const A& l, const A& r); 7 };
在operator*系以值传递的方式返回了一个计算结果,联系到条款20,我们自然会想到,那么用引用传递试试。
因为函数返回的是A对象,所以函数创建新对象的方式有2:
1.在stack空间创建
1 const A& operator* (const A& l, const A& r) { 2 A result(l.n * r.n, l.d * r.d); 3 return result; 4 }
这样我们避免值传递的调用构造和析构函数,可是result是个临时变量,在函数推出前就被销毁了。任何对这个函数的调用都会是无意义的行为。
2.在heap空间创建
1 const A& operator* (const A& l, const A& r) { 2 A* result = new A(l.n * r.n, l.d * r.d); 3 return *result; 4 }
这样解决了上面临时变量的问题。可是,这个new出来的对象什么时候被释放呢?
就算我们调用A的时候很谨慎,但还是避免不了出错,比如
1 A w,x,y,z; 2 w = x * y * z; // 实际与operator*(operator*(x, y), z)相同
这里同一个语句例调用了两次operator*,因此两次使用new,也就需要两次delete,但却没有合理的办法让operator*使用者进行那些delete调用。这就会导致资源泄露。
经过上面两种方法都不行,可能你还会想到这种方法:我声明一个static对象不行么?
比如:
1 const A& operator* (const A& l, const A& r) { 2 static A result; 3 result = ..... 4 return result; 5 }
看上去完美的解决了这个问题,那么我们这样使用的时候呢?
1 bool operator == (const A& l, const A& r); 2 A a,b,c,d; 3 if ((a*b) == (c*d)) { 4 .... 5 }
结果就会出现:(a*b) == (c*d)总是为true,无论a,b,c,d是什么!
将(a*b) == (c*d)拆开来理解,我们就可以知道为什么了。
operator == (operator*(a, b), operator(c*d));
因为到最后位置,都会返回static对象的现值,也就不怪乎为什么返回true了。
◆总结
1.绝不要返回pointer或者referenc指向一个local stack对象,或返回reference指向一个heap-allocated对象,或返回pointer或reference指向一个local static对象。