C++之必须返回对象时候,别妄想返回其reference(21)---《Effective C++》

3 篇文章 0 订阅
2 篇文章 0 订阅

条款21:必须返回对象时,别妄想返回其reference

上篇博客我们讲了pass-by-value效率特别低,除了一些特定情况时,尽量在传递参数的时候使用pass-by-reference-to-const,因此,有的小伙伴就走向了一个极端,坚定追求pass-by-reference纯度,以至于对于返回值也想设置为引用,然鹅物极必反,不用太追求极端的!下面我们来看一些例子,拨开各位小伙伴心中的迷雾!

class Rational{
public:
    Rational(int numerator=0,int denominator=1);
    ...
private:
    int n,d;
    friend const Rational operator*(const Rational& lhs,const Rational& rhs);//这里请大家不要过分关注于返回值是const    ,这是为了避免((a*b)=c)这样的手误,其中a,b,c都是Rational对象
};

这里我们看到程序返回的值为Rational,这样的话不就又要对返回值进行复制,又会消耗内存,那可不嘛?有什么办法呢?
要不要声明为返回值引用呢?
1)在栈空间创建对象
请参看如下代码:

const Rational& operator*(const Rational& lhs,const Rational& rhs){
    Rational result(lhs.n*rhs,lhs.d*rhs.d);
    return result;
}

这样的话就不用对返回值进行一个复制,效率不就高了吗?抱歉,老铁,这样想可就错啦,注意result是什么,local对象,但operator*函数调用结束后呢?早都释放啦,此时Rational&的地址中什么东西都没啦,只是一个残骸,如果是基本类型等没有析构函数的对象的话,运气好的话还可能存在一段时间,所以这种方法错误吧!
2)在堆空间创建对象

const Rational& operator*(const Rational& lhs,const Rational& rhs){
    Rational* result=new Rational(lhs.n*rhs.n,lhs.d*rhs.d);
    return *result;
}

这样的话,不就返回的值引用的东西就是head中的吗?没什么问题了吧?这是的确可以返回正确的值,然而呢?我们在堆中创建了对象,什么时候删除呀,老铁,删不掉了吧!尤其是像如下方式调用的时候:

Rational w,x,y,z;
w=x*y*z;

这下连续两次调用了operator*函数,因而两次使用new,也就需要两次使用delete方法,可是没办法delete呀,所以这种方法仍旧错误!
3)声明static变量作为返回引用的值

const Rational& operator*(const Rational& lhs,const Rational& rhs){
    static Rational result;
    result=...;
    return result;
}

这下不就可以解决问题了吗?哈哈哈,老铁,你又栽了,C++的坑多了去了,稍不注意就被坑啦,那么这种方式有什么问题呢?
多线程安全性有问题;
可能计算出的值也有问题,懵了吧,请看如下代码:

#include <iostream>
#include <string>
using namespace std;
int& hel(int t){
    static int i = t + 1;
    return i;
}

int main(){
    int i = 10, j = 100;
    cout << hel(i) << " " << hel(j) << endl;
    if (hel(i) == hel(j)){
        cout << "true" << endl;
    }
    else{
        cout << "false" << endl;
    }
    return 0;
}

运行结果如下:
这里写图片描述
可以发现,我们此时的值都相等,为什么呢?因为我们返回的都是static int i的值,而内部的static会被覆盖,所以我们永远看到的都是static的“现值”,因此,两个进行比较肯定都是相等的。

最终,我们才发现原来如果想对operator*这样的函数返回reference,除了采用最极端的方式,才可能实现,例如如下的这种方法:
可能会有比较厉害的小伙伴说我们可以在函数内部声明一个数组,然后返回数组中不同的索引值,可以通过这种复杂的方式最终实现的,那么这样开辟数组空间,大小不能确定,同时复制操作又要浪费时间,构造函数析构函数调用消耗资源,是不是得不偿失呀,大兄弟!

类似于operator*这种函数,我们最推荐的方法是返回对象,而不是拼命返回引用,造成得不偿失或者出错的问题!

const Rational operator*(const Rational& lhs,const Rational& rhs){
    return Rational(lhs.n*rhs,lhs.d*rhs.d);
}

总结:
绝不要返回pointer或者reference指向一个local stack对象,或者返回reference指向一个heap-allocated对象,或者返回reference或者pointer指向local static对象,让我们必须在“返回一个reference和返回一个object”之间加以权衡的话,我们需要谨慎决定!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值