C++知识小屋(2):返回对象还是返回引用?我与运算符重载的十个故事

       类可以说是面向对象的非常重要的思想,在C++这门课程里面,几乎都是围绕类来展开学习的。既然类这么重要,那么跟类相关的知识必然也非常多。所以想在这里把与类相关的知识梳理成一个框架,这样子在面对一个具体问题的时候,就能够知道对应哪部分的知识,应该怎么去使用其中的函数,功能。C++知识小屋🏠系列希望能够对C++的类和对象有一个全面的梳理,同时会给出相关的例子对应知识点,试图用最简单的语言带你一步步掌握晦涩难懂C++的知识。

前情提要

       在最近的上机实验中,遇到了挺多关于运算符重载🚌的内容,但是这部分内容涉及到的细节实际上非常多。
1.返回类型是否要加引用?
2.友元函数重载运算符跟成员函数重载运算符的区别是什么?
3.老师上课说的局部对象不能返回引用是什么意思呢?
这一节内容主要对这些细节进行一下梳理,虽说在正式上机做题的过程中遇到错误试一试总能改对,但总是抱着试一试的想法而不了解其背后的原理感觉还是不太好,所以下面就来一一解释,跟着我的节奏来吧🚀。

问题引入

问题描述

  • 金属具有硬度、重量、体积的属性(都是整数)
  • 合并:每两块金属可以合并成一块新的金属。新金属的重量等于原两块金属的重量之和,体积和硬度也类似计算。
  • 用加法运算符的方式实现合并
  • 输入:
    • 第一行输入第一块金属的信息,包括硬度、重量、体积
      第二行输入第二块金属的信息,包括硬度、重量、体积
    • 3 3000 300
      5 5000 500
  • 输出:
    • 合并后的金属信息
    • 硬度8–重量8000–体积800
#include <iostream>
using namespace std;

class metal {
private:
    int hardness,weight,volume;
public:
    metal(int h,int w,int v):hardness(h),weight(w),volume(v){}
    
	//故事一、友元函数重载'+'运算符
    friend metal operator+(const metal &m1,const metal &m2){
      int h,w,v;
      h = m1.hardness+m2.hardness ;
      w = m1.weight+m2.weight;
      v = m1.volume + m2.volume;
      return metal(h,w,v);
    }
	
	//输出函数
    void print(){
      cout<<"硬度"<<hardness<<"--重量"<<weight<<"--体积"<<volume<<endl;
    }
};

int main() {
    int h,w,v,n;
    cin>>h>>w>>v;
    metal m1(h,w,v);
    cin>>h>>w>>v;
    metal m2(h,w,v);
    (m1+m2).print();
    return 0;
}

在这里插入图片描述


故事1、’+'运算符重载解析(✅)

  • 在上面的代码中,实现了对两个对象的’+'的运算符重载,我们单独拿出来看。
  • 首先这是一个友元函数(前面加了friend),因为友元函数不属于任何一个类,因此我们不能够用this指针表示当前对象的数据成员。
  • 由于加法涉及到两个操作数,比如a+b,这里的a和b各自代表一个操作数,结合前面说的友元函数不能表示当前对象->因此该友元函数重载’+'的参数需要有两个对象。
    • 该例子中的参数为const metal & m1,实际上“const 类型 & 对象名”代表着该对象在该函数里面不能被改变,否则就会报错,这么写是一个习惯问题。
    • 当然也可以写成metal m1.
  • 在该函数的内部首先创建三个新的变量h,w,v,然后将其表示为两个参数对应的值的和。接着返回一个临时创建的对象metal,其中使用有参构造函数的方法,将前面构造好的新的h,w,v赋值给这个新的对象。
  • 以上即为该函数的全部解析👆
//故事一、友元函数重载'+'运算符🗼
    friend metal operator+(const metal &m1,const metal &m2){
      int h,w,v;
      h = m1.hardness+m2.hardness ;
      w = m1.weight+m2.weight;
      v = m1.volume + m2.volume;
      return metal(h,w,v);
    }

使用友元函数重载’+'的其他写法(有对有错)

上面使用友元函数重载’+'运算符的代码实际上还有很多种别的写法,下来就来看看吧。

故事2、函数内创建新的对象,返回该对象(✅)

  • 与上面的写法一个意思,只是在返回之前先创造了一个对象C,而上面的方法直接在返回中创建。
	//故事二、友元函数重载'+'运算符
    friend metal operator+(const metal &m1,const metal &m2){
      int h,w,v;
      h = m1.hardness+m2.hardness ;
      w = m1.weight+m2.weight;
      v = m1.volume + m2.volume;
      metal C(h,w,v);				//创建新的对象
      return C;						//返回该新的对象
    }

故事3、函数内创建新的对象,返回该对象的引用(编译器报WARNING,但能通过)(⭕)

  • 使用dev编译器编译会报警告,但是能通过最好不要这么写,因为函数内部创建的局部对象不能作为引用返回。
	//故事三、友元函数重载'+'运算符,注意下面加了个&符号噢!!!!
    friend metal & operator+(const metal &m1,const metal &m2){
      int h,w,v;
      h = m1.hardness+m2.hardness ;
      w = m1.weight+m2.weight;
      v = m1.volume + m2.volume;
      metal C(h,w,v);				//创建新的对象
      return C;						//返回该新的对象的引用(函数定义时为返回引用)
    }

编译器的警告:
在这里插入图片描述


故事4、返回临时创建的对象,返回类型为引用(❌)

  • 这么写的话会报错,我个人的理解是由于是在返回过程中创建的对象用来返回,此时还没有完全创建成功该对象,所以返回引用的话其地址是不确定的。

在这里插入图片描述


用成员函数的方法写

  • 上面的情况都是使用友元函数的方法来写的,那么能不能用普通的成员函数重载运算符呢?那肯定可以的。
  • 成员函数重载运算符的特点如下:
    • 对于’+'来说,比如a+b,在成员函数的写法中,参数只需要告诉它b是什么,而a默认由当前对象的this指针代替。因此参数只有一个。

故事5、成员函数重载运算符+返回新对象(✅)

	//故事五、用成员函数的方法写
     metal operator+(const metal &m2){	//成员函数
      int h,w,v;
      h = hardness+m2.hardness ;		//当前对象的数据成员直接访问
      w = weight+m2.weight;
      v = volume + m2.volume;
      metal C(h,w,v);					//创建新的对象
      return C;							//返回新的对象
    }

故事6、成员函数重载运算符+返回新对象引用(⭕)

  • 该方法返回的是一句局部对象的引用,与故事三一样,能够通过编译,不过会报警告。
  • 再次强调,函数的局部变量不可以作为引用返回。
	//故事六、用成员函数的方法写,返回类型为引用(下面多了个&)
     metal & operator+(const metal &m2){//成员函数,返回类型为引用
      int h,w,v;
      h = hardness+m2.hardness ;		//当前对象的数据成员直接访问
      w = weight+m2.weight;
      v = volume + m2.volume;
      metal C(h,w,v);					//创建新的对象
      return C;							//返回新的对象的引用
    }

编译器返回的警告:
在这里插入图片描述


故事7、成员函数重载运算符+返回this指针(当前对象的值发生改变)(✅)

  • 这种方法是对当前对象进行操作,因此当前对象的值会发生该变。
  • 如果当前对象进行完该加法之后不再用得上,这么写则没什么问题。
  • 如果当前对象进行完该加法后仍需要用来做别的事情,那么使用这种方法,原来的对象会发生改变,需要提前进行拷贝才行
  //故事七、用成员函数的方法写,返回类型当前对象的指针
  metal operator+(const metal &m2){
      hardness += m2.hardness;			//在当前对象上进行改变
      w = weight += m2.weight;			//在当前对象上进行改变
      v = volume += m2.volume;			//在当前对象上进行改变  
      return *this;						//返回当前对象
    }

故事8、成员函数重载运算符+返回this指针的引用(当前对象的值改变,效率更高)(✅)

  • 该方法比起4.3的方法,在于其返回的是当前对象的引用,而4.3的方法返回当前对象实际上调用了一次拷贝构造函数,效率低了一些。该方法直接返回当前对象,效率更高。
  //故事八、用成员函数的方法写,返回类型当前对象的指针的引用
  metal & operator+(const metal &m2){
      hardness += m2.hardness;			//在当前对象上进行改变
      w = weight += m2.weight;			//在当前对象上进行改变
      v = volume += m2.volume;			//在当前对象上进行改变  
      return *this;						//返回当前对象的引用
    }

故事9、成员函数重载运算符+返回参数的对象(参数对象不发生改变)(✅)

  • 在这种方法中,我们将参数m2由const metal &m2变成metal m2,这样子我们就能够改变m2的内部数据成员,然后我们将改变后的m2对象进行返回。
  • 因为我们的参数是m2,传进来的对象是真实的m2对象的一个拷贝,所以进行完该函数后原本的m2是不发生任何改变的。
  • 如果参数写成metal &m2,则进行完该函数后原本的m2会发生改变。
	//故事九、用成员函数的方法写,返回类型参数对象更新后的对象
    metal operator+(metal m2){
      m2.hardness += hardness;
      m2.weight += weight;
      m2.volume += volume;  
      return m2;
    }

故事10、成员函数重载运算符+参数对象为引用(参数对象发生改变)(✅)

  • 在这种方法中,参数为m2的引用,则我们在函数内部对m2的改变是会影响到真实的m2的值的。
	//故事十、用成员函数的方法写,返回类型参数对象更新后的对象,参数为m2的引用
    metal & operator+(metal & m2){
      m2.hardness += hardness;
      m2.weight += weight;
      m2.volume += volume;  
      return m2;
    }

总结

  • 再来回顾一下运算符重载的函数定义格式
    • 是否为友元) 返回类型 operator 重载的运算符 (参数),下面举几个🌰回顾一下
      在这里插入图片描述
  • 上面列举了十种重载运算符的写法,总结下来包括一下三个层面:
    • 使用友元函数 OR 使用成员函数
      • 友元函数重载’+'的参数需要两个(故事1-4)
      • 成员函数重载’+'的参数只需要一个(另一个用当前对象this表示) (故事5-10)
    • 返回对象 OR 返回引用
      • 函数内的局部变量不能够以引用的形式返回(部分编译器能通过)(故事3,6)
      • 返回this指针的方法中,返回引用比不返回引用的效率更高 (故事7,8)
    • 返回过程中创建临时对象 OR 先创建新对象再进行返回
      • 二者的本质其实一样,都是返回一个新的变量(故事1,2)
      • 对于返回过程中创建临时对象的方法,如果采用返回引用的方式,编译器报错。(故事3)

如果觉得上面讲得还不错的话,不妨来个点赞关注收藏三连噢😜

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值