版权声明:转载请注明出处,谢谢!!! https://blog.csdn.net/qhdhnbd110/article/details/83586088
在条款20,我们讨论了pass-by-value与pass-by-reference-to-const的传参效率问题,结果是对于自定义类型,pass-by-reference-to-const方式往往更加高效,那么我们就会想到,如果返回值也利用pass-by-reference-to-const方式返回,那会不会更好一点呢?
考虑下面Rational类实现了两个有理数的乘积:
#include<iostream>
using namespace std;
class Rational
{
public:
Rational(int n = 0, int d = 1)
{
this->n = n;
this->d = d;
}
~Rational(){}
private:
int n, d; //n为分子,d为分母
public:
friend const Rational operator*(const Rational &lhs, const Rational &rhs);
};
inline const Rational operator*(const Rational &lhs, const Rational &rhs)
{
return Rational(lhs.n * rhs.n, lhs.d * rhs.d);
}
int main()
{
Rational a(1, 2);
Rational b(4, 7);
Rational c = a * b;
return 0;
}
1. 修改为返回一个reference
inline const Rational &operator*(const Rational &lhs, const Rational &rhs)
{
Rational Result(lhs.n * rhs.n, lhs.d * rhs.d);
return Result;
}
如果我们返回了一个引用,那么它肯定是一个对象的别名,这个对象就是Result,但是注意,这个对象是一个局部变量,定义在栈帧中,也就是说,函数结束后,其会被自动销毁,那么返回的引用意味着代表了一块被释放的区域,这会引发一系列的无法预测的情况。
2. 修改为返回一个在堆中申请的对象
inline const Rational &operator*(const Rational &lhs, const Rational &rhs)
{
Rational Result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d);
return *Result;
}
这样我们就避免了局部变量销毁后带来的麻烦,但是考虑:
Rational a(1, 2);
Rational b(3, 4);
Rational c(5, 6);
Rational d = a * b * c;
这种情况下,b * c的返回值是一个堆中对象,那么我们就没有办法得到这个对象的指针,也就无法将其销毁,造成资源泄漏。
3. 修改为返回一个静态变量的引用
inline const Rational &operator*(const Rational &lhs, const Rational &rhs)
{
static Rational Result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d);
return *Result;
}
首先不考虑在多线程情况下的安全性,考虑用户合理的代码:
Rational a(1, 2);
Rational b(3, 4);
Rational c(5, 6);
Rational d(7, 8);
if((a * b) == (c * d))
//...
你会发现a,b,c,d是什么值,if中的判断永远都是true。
将if中的语句改为等价的函数调用形式:
if(operator == (operator *(a, b), operator *(c, d)))
虽然两个operator*操作都各自修改了Result的值,但是在operator==调用时,两者的返回值都是同一个位置的引用,所以两者永远是相等的。
结论:当你需要在返回一个reference和一个object之间做出选择时,只需选择行为正确的那个就好,优化就交给编译器就好。