前一小节已经讨论过,pass by vaule的代价有时候是巨大的,pass by refrence比较方便。那么肯定也有人会立刻想到,函数返回值的时候,能不能也采用这种办法来提高程序的效率呢?
首先,返回局部变量的引用是不对的。因为如果你返回一个引用,引用肯定是一个对象的别名,那么你一定要先反问自己:这个引用所代表的实际值到底是谁呢?想到这里,我们就一目了然了:你返回的引用是局部变量i的别名,但是i在函数结束以后,就被释放了!所以此时的返回的是一个垃圾数字。这个程序奇怪的地方在于,如果把doNothing函数注释掉,那么程序竟然能得出正确的值,这里的奥秘在于如果调用了函数,那么栈的内容有可能发生改变,改变之后,程序就得出合理的错误值了。而当这个函数调用被注释掉以后,由于栈的内容没有改变,所以即使i已经被释放了,但是它并没有被清零,值还是3,我还是可以引用它。类似的例子还有就是返回局部变量的指针:
也许,有人会想到,那么我让p指针时堆上分配的内存不就行了?
但这样会则要求函数的调用者手动释放内存,否则会造成内存泄露。最致命的是,有时这种泄露是无法被释放的!
比如下面这个计算有理数的类:
如果我这样调用:
那么t2*t3所new的指针就没办法释放了!
也许有人自作聪明的想到,让返回的引用指向一个定义域函数内部的static对象:
假如你又定义了比较操作符:
但是如果你很快就会发现:if( (t1 * t2) == (t3 * t4))这样的语句总是为true。
原因在于(t1 * t2)会修改reslt的值,而(t3 * t4)又会修改result的值。总之,你始终是在做result == result 的判断,当然为true了。
总结起来就是:绝不要返回一个指向局部对象的指针或者引用,也不要返回指向分配在堆上的对象,也不要返回全局或者静态对象。返回值还是pass by value比较好。
为了能够简单且说明问题,这里选择了对于内置类型返回其reference:
int& func()
{
int i=3;
return i;
}
void doNothing(int i)
{
}
int main()
{
int& k=func();
doNothing(7); //注释此行,看看有何区别
cout <<k <<endl;
return 0;
}
首先,返回局部变量的引用是不对的。因为如果你返回一个引用,引用肯定是一个对象的别名,那么你一定要先反问自己:这个引用所代表的实际值到底是谁呢?想到这里,我们就一目了然了:你返回的引用是局部变量i的别名,但是i在函数结束以后,就被释放了!所以此时的返回的是一个垃圾数字。这个程序奇怪的地方在于,如果把doNothing函数注释掉,那么程序竟然能得出正确的值,这里的奥秘在于如果调用了函数,那么栈的内容有可能发生改变,改变之后,程序就得出合理的错误值了。而当这个函数调用被注释掉以后,由于栈的内容没有改变,所以即使i已经被释放了,但是它并没有被清零,值还是3,我还是可以引用它。类似的例子还有就是返回局部变量的指针:
int* func()
{
int i=3;
int *p = &i;
return p;
}
int main()
{
int *p = func();
doNothing(1);
cout<<*p<<endl;
return 0;
}
也许,有人会想到,那么我让p指针时堆上分配的内存不就行了?
int* func()
{
int *p = new int(3);
return p;
}
但这样会则要求函数的调用者手动释放内存,否则会造成内存泄露。最致命的是,有时这种泄露是无法被释放的!
比如下面这个计算有理数的类:
class Rational
{
public:
Rational(int numerrator = 0, int denominator = 1):n(numerrator),d(denominator){}
double getVal()
{
double result = (double)n / double(d);
return result;
}
friend Rational& operator*(const Rational &lhs,const Rational &rhs);
private:
int n;//分子
int d;//分母
};
Rational& operator*(const Rational &lhs,const Rational &rhs)
{
Rational* result = new Rational(lhs.n*rhs.n,lhs.d*rhs.d);
return *result;
}
如果我这样调用:
Rational t1(1,1);
Rational t2(1,2);
Rational t3(1,5);
Rational t4 = t1*t2*t3;
那么t2*t3所new的指针就没办法释放了!
也许有人自作聪明的想到,让返回的引用指向一个定义域函数内部的static对象:
Rational& operator*(const Rational &lhs,const Rational &rhs)
{
static Rational result;
result = Rational(lhs.n*rhs.n,lhs.d*rhs.d);
return result;
}
假如你又定义了比较操作符:
bool operator==(const Rational &rhs)
{
return n == rhs.n && d == rhs.d;
}
但是如果你很快就会发现:if( (t1 * t2) == (t3 * t4))这样的语句总是为true。
原因在于(t1 * t2)会修改reslt的值,而(t3 * t4)又会修改result的值。总之,你始终是在做result == result 的判断,当然为true了。
总结起来就是:绝不要返回一个指向局部对象的指针或者引用,也不要返回指向分配在堆上的对象,也不要返回全局或者静态对象。返回值还是pass by value比较好。