知道了pass -by-value(传值)的效率牵连层面,从此一直适用pass-by-reference,就会犯下一个致命错误:开始传递一些reference指向其实并不存在的对象;考虑如下代码:
class Rational {
public:
Rational(int numberator = 0, int denominator = 1);
//...
private:
int n, d;
friend const Rational operator* (const Rational& lhs, const Rational &rhs);
};
此版本的operator *以by value方式返回其计算结果(有构造和析构成本),如果返回其reference,就不需要付出该代价,如果要返回reference,就代表该对象已经存在了(一定是某物的另一个名称,指向某个既有的Rational对象);
函数创建新对象有两种途径:在stack空间或在heap空间创建;
// 在stack空间创建
const Rational& operator* (const Rational& lhs, const Rational& rhs) {
Rational result(lhs.n * rhs.n, lhs.d * rhs.d);
return result;
}
上述函数问题是返回一个reference指向result,但是result是个local对象,而local对象在函数退出前被销毁了
// 在heap空间创建
const Rational& operator* (const Rational& lhs, const Rational& rhs) {
Rational* result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d);
return *result;
}
Rational w, x, y, z;
w = x * y * z;
上述函数还是必须付出一个"构造函数调用"代价,但是又出现了另外一个问题,谁该对着被你new出来的对象(result)实施delete?(无法解决,因为没有合理的办法让他们取得operator *返回的references背后隐藏的那个指针).
无论是on-the-stack还是on-the-top,都因为对operator *返回结果调用构造函数而收到惩罚;或许你又想到了"让operator *返回的reference指向一个被定义于函数内部的static Rational对象"
const Rational& operator* (const Rational& lhs, const Rational& rhs) {
static Rational result;
result = ...;
return result;
}
像所有用上static对象的设计一样,会造成对多线程安全性的疑虑,还有如下问题,考虑代码
bool operator==(const Rational& lhs, const Rational& rhs);
Rational a, b, c, d;
if ((a * b) == (c * d)) {
// 乘积相等时...
} else {
// 乘积不相等时...
}
上述代码永远是相等的,因为每个reference指向operator *内部定义的static Rational对象,(两次operator *调用的确各自改变了static Rational对象值,但是由于它们返回的都是reference,因此调用端看到的永远是static Rational对象的"现值" )