Effective C++条款21:设计与声明之(必须返回对象时,别妄想返回其reference)

  • 前面一篇文章我们介绍了,向函数中传递对象时,应以const引用的方式传递它,但是并不是在任何情况下使用引用都是正确的,文本来介绍一些情况

一、演示说明

  • 现在我们有一个用来变现有理数的class,内含一个函数用来计算两个有理数的乘积
  • 下面代码中的operator*是正确的,也是合理的
class Rational
{
public:
    Rational(int numerator = 0, int denominator = 1);
private:
    int n, d; //分子和分母
    friend const Rational 
        operator*(const Rational& lhs, const Rational& rhs);
};

错误实现:返回一个在栈上的局部变量

  • 下面我们修改operator*函数,使其在函数内部建立一个对象(栈上),然后返回其引用
  • 错误原因:由于result是局部对象,函数结束之后局部对象(栈空间)就释放了,引起返回其引用是无效的,也就是说该函数返回了一个已经无效的对象的引用,因此使用起来是错误的
friend const Rational&
    operator*(const Rational& lhs, const Rational& rhs)
{
    Rational result(lhs.n*rhs.n, lhs.d*rhs.d);
    return result; //虽然编译器不报错,但是逻辑上是错误的
}

错误实现:返回一个在堆上的对象(内存泄漏)

  • 如果我们在堆上申请一个对象,然后将其引用返回,虽然编译不出错,但是使用起来也是错误的
  • 错误原因:在函数内,我们在堆上申请了一个对象,然后返回对象的引用,虽然函数结束之后对象仍存在,我们使用起来不会出错,但是这个函数内部建立的对象我们无法释放(因为我们无法获取其指针,不能进行delete操作)。因此会造成内存泄漏
friend const Rational&
    operator*(const Rational& lhs, const Rational& rhs)
{
    Rational *result = new Rational(lhs.n*rhs.n, lhs.d*rhs.d);
    return *result;//虽然编译器不报错,但是逻辑上是错误的
}
  • 例如我们书写下面的代码,调用了两次operator*,但是我们无法获取在内部申请的result对象的指针,因此就无法释放这两个对象。因此造成内存泄漏
Rational w, x, y, z;
w = x*y*z; //相当于operator*(operator*(x,y),z);

错误实现:返回一个静态局部变量

  • 用户还可能定义一个静态局部变量,然后将其引用返回,这样也是错误的
  • 错误原因:我们知道静态局部变量有这样的一个特点,第一次定义之后就保存在程序中(即使函数结束依然存在,知道程序退出才释放),因此当下一次再遇到静态局部变量定义语句的时候不会再执行,因为这个对象已经存在了。那么当我们每回调用operator*函数的时候,每回返回的都是同一个静态局部变量的引用
friend const Rational&
    operator*(const Rational& lhs, const Rational& rhs)
{
    static Rational result; //静态局部变量

    //result=...; 将lhs乘以rhs,然后将结果保存在result中

    return *result; //虽然编译器不报错,但是逻辑上是错误的
}
  •  现在我们有下面的代码,那么下面的if语句一定永远为真,因为上面的operator*函数返回的都是同一个对象的引用(即使调用operator*之后result的值可能改变,但是此处比较的是对象,而不是对象的值),因此比较的时候对象时相同的,那么if就为真
//针对Rational而写的operator==
bool operator==(const Rational& lhs,const Rational& rhs);

int main()
{
    Rational a,b,c,d;
    //...
    if((a*b)==(c*d)){ //此if永远为真,因为operator*返回的都是同一个对象的引用

    }
    else{

    }
}

二、正确的做法

  • 正确的做法就是如果函数不建议返回对应的引用,那么就一定不能返回对象的引用,应该返回一个对象的副本(虽然会造成额外的构造与析构,但是是值得的)

三、总结

  • 绝不要返回指针/引用指向一个局部stack对象,或返回一个引用指向heap-allocated对象,或者返回一个引用/指针指向一个静态局部变量
  • 条款4已经为“在单线程中合理返回引用指向一个静态局部变量”提供了一份设计实例
发布了1462 篇原创文章 · 获赞 996 · 访问量 35万+

猜你喜欢

转载自blog.csdn.net/qq_41453285/article/details/104275076