在上一篇博客中,我们介绍了简单地使用值传递带来的种种麻烦,相信有些朋友会一心一意将其斩草除根,但是当返回值也使用了引用的时候,麻烦就来了。
依然来个简答的例子
class Rational
{
public:
Rational(int numerator = 0,int denominator = 1);
private:
const Rational& operator*(const Rational& lhs,const Rational& rhs);
};
operator的返回值是一个reference。记得那句话吗?“引用是另外一个变量的别名”,那么问题就来了,operator返回的引用是谁的别名。假如我们返回的是一个局部变量,那么这个家伙在函数返回的时候实际上已经不存在了。也就是说,这个时候返回的reference,是“以前的,已经不存在的一个变量的别名”。而假如我们使用一个不存在的东西,我们也将一败涂地。这个时候,我们考虑在heap(堆)上创建一个对象。
const Rational& operator*(const Rational& lhs,const Rational& rhs)
{
Rational *result = new Rational();
return *result;
}
让我们回溯到提醒我们使用引用的原因上:避免太大的消耗。这个主要体现在构造函数与析构函数上,而上述的例子中,很明显我们又回到了解决问题之前,又得付出一个构造函数的代价。但是,更不妥的是,下面这个例子。
Rational w,x,y,z;
w = x * y * z;
x * y将会在heap上new Rational()产生一个Rational_1,然后这个Rational_1的引用与z又将会在heap上new Rational()产生一个Rational_2,然后这个Rational_2将会被w持有。在程序结束的时候,我们使用delete w销毁了Rational_2,那么,Rational_1呢?很明显,我们造成了内存泄露。
从上面的描述我们可以发现,不管是在stack或者在heap上,返回一个reference都会受到惩罚。那么函数返回一个对象的正确写法是什么呢?就是”让函数返回一个对象呗“虽然,这有可能给性能造成一定的拖累,但是它能保证这是正确的。