类可以说是面向对象的非常重要的思想,在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 重载的运算符 (参数),下面举几个🌰回顾一下
- (是否为友元) 返回类型 operator 重载的运算符 (参数),下面举几个🌰回顾一下
- 上面列举了十种重载运算符的写法,总结下来包括一下三个层面:
- 使用友元函数 OR 使用成员函数
- 友元函数重载’+'的参数需要两个(故事1-4)
- 成员函数重载’+'的参数只需要一个(另一个用当前对象this表示) (故事5-10)
- 返回对象 OR 返回引用
- 函数内的局部变量不能够以引用的形式返回(部分编译器能通过)(故事3,6)
- 返回this指针的方法中,返回引用比不返回引用的效率更高 (故事7,8)
- 返回过程中创建临时对象 OR 先创建新对象再进行返回
- 二者的本质其实一样,都是返回一个新的变量(故事1,2)
- 对于返回过程中创建临时对象的方法,如果采用返回引用的方式,编译器报错。(故事3)
- 使用友元函数 OR 使用成员函数
如果觉得上面讲得还不错的话,不妨来个点赞关注收藏三连噢😜