[C++] 类和对象(3): 赋值重载、运算符重载...
一、运算符重载
内置类型数据或表达式
进行操作的运算符重载
,使同类对象之间可以使用运算符,进而提高代码可读性
否则对象之间,一个简单的比较大小就得需要长代码实现. 如果需要多次比较,将十分痛苦1.1 运算符重载定义
运算符重载
其实是一种具有特殊函数名
的函数,其 返回值类型
与 参数类型
同一般函数一样
但是 运算符重载函数的函数名格式为:operator运算符
即,operator + 需要重载的运算符
则函数的原型格式就为:返回值类型 operator运算符(参数列表)
不能创造新的运算符
:比如 通过operator@
想要赋予@
一定的功能重载运算符必须至少有一个自定义类型的操作数
:操作符必须要有操作数重载后的运算符不能改变原有含义
:不能将原来内置类型+
的功能,重载之后改为 对象之间相减的功能.*
、::
、sizeof
、? :
、.
以上5个运算符不能重载(笔试、面试会考)
- 由于类内存在隐含的
this指针
,所以参数要比类外少一个,参数要比运算符所需操作数少一个,且默认 this指针 为第一个参数
(这也是重载需要严格遵守的第五条规则)
日期类判断大于
为例:bool operator>(const Date& d1, const Date& d2) //比较大小不需要改变数值,所以const修饰,&可以节省资源
{
// 日期比较大小需要遵循实际
return (d1._year > d2._year
|| (d1._year == d2._year && d1._month > d2._month)
|| (d1._year == d2._year && d1._month == d2._month && d1._day > d2._day));
}
运算符重载函数是可以定义在类内
的,不过与定义在类外有一定的区别:传参使用
:操作符正常逻辑
使用也是没有问题的重载后的操作符,可以按照 正常的逻辑 使用 但其实,编译器还是会把使用操作符的语句,自动转换成这样
d1.operator>(d2)
或 这样operator>(d1,d2)
,不用手动操作
1.2 赋值运算符重载
有一些需要非常注意的问题
:为什么赋值运算符重载函数,需要返回赋值后的结果
?内置类型的赋值运算符(=)
结果也是需要当作返回值返回的,因为需要实现连等(连续赋值)操作:赋值操作的结果要作为返回值返回,以便下一次赋值使用
,所以,将 *this
作为返回值返回返回值是
Date&
类型的,使用引用是因为可以节省资源 但是并不是任何时候都可以使用引用类型作为返回值类型,因为 函数内的局部变量出了作用域就销毁了,如果再用&
只会造成错误 这里使用&
,是因为this指针
没被销毁
赋值重载函数也是 类的默认成员函数
一个类,如果没有显式定义赋值重载函数,编译器也会自动生成一个赋值重载函数,赋值操作按字节拷贝完成
1.2.1 思考
d1
已存在):Date D = d1;
- 赋值重载:将一个对象的内容 赋值于 另一个对象
- 拷贝构造:在对象实例化时,将一个对象的内容 初始化至 正在实例化的对象
Date D = d1
调用的是哪个函数了拷贝构造函数
,因为这是在对象实例化时,要进行的初始化
。编译器会分析出来并自动调用拷贝构造函数Date d2 = d1;
这个语句被执行时调用的是 拷贝构造函数
;
而 d2
被实例化之后,再执行 d2 = d1
,则调用 赋值重载函数
.1.3 日期类及运算符重载实现
==
、>=
、!=
、<
、<=
、+
、-
、+=
、-=
、++
、--
等==
开始逐一实现==
>
或 <
的重载 和 ==
的重载之后,其他的逻辑判断运算符,都可以直接复用 >
和==
或 <
和 ==
来实现>=、!=、<、<=:
+ (日期 + 天数)
需要存在一定的意义
2002.1.1
+ 2000.1.1
有什么实际意义呢?+
一般实现的是,日期 + 天数
的功能31天
;
4、6、9、11月:30天
;
2月:一般28天,闰年29天
具体思路是:
- 先把需要加的天数全部加到对象的_day上
- 然后逐月计算,完成月份和年份的进位
+
的重载之前,先实现一个通过年月来计算月份天数的函数:+
重载:- 先将要加的天数加到
_day
上 - 循环判断当前的
_day
是否大于 当前月的实际天数 - 如果大于,
_day
就减去当前月的实际天数,并且月份加一(_month++)
- 每次
_month == 13
时,表示本年过完,需要年份加一(_year++),且月份归一(_month = 1)
- 直到
_day
不大于 当前月的实际天数,返回临时对象
需要将临时对象作为返回值返回,因为需要保证连加操作
- 不能直接操作
this指针
所指对象,否则会导致原对象数值改变 - 拷贝的临时对象出函数会被销毁,所以不能用
&类型
作为返回值
+= (日期 += 天数)
+
的重载一样,+=
通常也是实现 日期 += 天数
+
完全相同,不过 因为+=需要改变原对象数值
,所以可以直接操作 this指针 指向的对象
也就意味着,可以将 *this
作为返回值返回,并且可以使用 &类型
节省资源- (日期 - 天数)
-
的重载其实可以有两种重载方式:- 日期 - 天数 ---> 日期
- 日期 - 日期 ---> 天数
- 如果
需要减去的天数(day)
比当前_day
大或两者相等,进循环;否则直接拿_day - day
就可以结束了 - 进循环之后,先用
day
- 当前的_day
,并且月份 - 1(--_month)
,代表减一个月 - 减一个月后,如果
_month == 0
,就代表这一年没了,年份需要减一(--_year)
,并且设置月份从12月开始(_month = 12)
- 再将当前天数,设置为当前月份天数,继续循环判断
- 当
day
不再大于_day
时,退出循环,然后_day - day
. 至此,天数减完毕. 然后返回临时变量
需要将临时对象作为返回值返回,因为需要保证连减操作
+
的重载相同:- 不能直接操作
this指针
所指对象,否则会导致原对象数值改变 - 拷贝的临时对象出函数会被销毁,所以不能用
&类型
作为返回值
-= (日期 -= 天数)
-=
的逻辑与 -
相同,也不过是直接操作*this
而已+、+=、-、-= 之间的复用及完善
+
、+=
、-
、-=
的重载函数之后,其实可以发现 +
和+=
、-
和-=
之间其实是有一定的关系的+
和+=
为例对比:ret的成员变量
,另一个操作 this指向的成员变量
所以,这+
和 +=
其实是可以互相复用的:+= 复用 +
和 + 复用 +=
哪个更优一点?+ 复用 +=
更优一点;
因为,+
的重载需要调用两次拷贝构造函数。直接实现+=
重载,是不需要调用拷贝构造函数的;如果 +=
复用 +
,就意味着 +=
同样需要调用两次拷贝构造函数;
如果是,+ 复用 +=
,则只有 +
需要调用拷贝构造函数-
和 -=
同样是这样的关系+
的重载 一般复用 +=
实现,-
的重载 一般复用-=
实现+
+=
-
-=
的重载 都只实现了对正数的操作,无法实现 对象 与 负数
的运算+
+=
-
-=
的重载之后,添加对负数的运算也只不过是加一个条件的事:+=
和 -=
,所以只需要在 +=
和 -=
的重载函数中添加条件就可以了—、++
--
和 ++
分有前置和后置--
或 ++
,要在参数列表中添加一个 int
类型--
和++
的重载非常的简单:- (日期 - 日期)
-
的重载还有一种意义,就是 两日期对象相减求相差多少天
-
的重载相对另一个,稍微简单一些:二、const 成员
const
修饰的变量无法被修改变量的权限可以缩小但是不能放大
比如:不能对被const修饰
的变量,起一个不被const修饰
的别名. 不能放大权限被const修饰
的对象,或者函数的参数使被const修饰
的类
当一个对象 被const
修饰 时,表示对象的成员不能被修改,则其成员函数很可能就不能正常的使用了将对象的地址作为this指针的内容传参到成员函数中
this指针
的类型(此类中)为 Date* const this
;
而此时的对象是被const
修饰的对象,此对象传参作为this指针
的内容,是使其从const Date
转换到Date
是属于权限的放大,是不能成立的this指针
是由编译器控制的,无法手动更改const对象
也正常的使用成员函数呢?2.1 const修饰成员函数
const
其实可以修饰成员函数。当 const
修饰成员函数时,此函数被称为 const
成员函数const
修饰成员函数时,所处的位置是在参数列表的后边,即:const
修饰时,即使是 const
对象也能够正常的使用成员函数了const
修饰对象的地址可以作为 this指针
的内容了,这又是为什么呢?const
修饰成员函数 就是 修饰了成员函数的 this指针
,即:this指针
的类型,但是 const
修饰成员函数就是 const
修饰了 this指针
。
编译器会将 const
转换为修饰 this指针
思考1 *
-
const
对象 可以调用 非const
成员函数吗?经过上面的介绍,可以得出答案是:不可以
因为 非
const
成员函数 意味着this指针
未被const
修饰const
对象 无法传参,所以不可以 -
非
const
对象 可以调用const
成员函数吗?答案是可以,因为
const
对象可以传参给const
成员函数(const修饰的this指针)
-
const
成员函数 可以调用其他 非const
成员函数吗?答案是不可以,
const
成员变量,其this指针
也是被const
修饰的调用其他成员函数,也就是使
this指针
的内容作为参数又是一种
const
对象无法传参,所以不可以 -
非
const
成员函数 可以调用其他const
成员函数吗?答案是可以,
const
对象 可以传参给const
修饰的this指针 -
const
什么时候应该修饰成员函数?const
修饰成员函数,其实本质是:const 类* const this
使
this指针
的内容无法改变所以,其实
当成员函数不改变对象的成员变量时,一般用const修饰此成员函数
思考2 *
(d1 + 100).print();
结合上面+
的重载,这个语句有什么问题?(假设d1为已实例化的对象,print()是其成员函数且未被const修饰)
Date operator+(int day);
+
的重载的返回值是 Date类型的,即+
的重载是传值返回,而这意味着 计算完成的结果 其实被作为了一个临时的对象
存储了起来在之前的文章中介绍过:
其实,函数一个局部变量作为返回值传值返回时,由于局部变量会被销毁,所以返回结果会被作为临时变量存储起来 这个临时变量是无法被修改的,具有
常性
,即可看作为const
修饰的
const
成员函数的当前 VS 编译器无法演示 (Linux) 好像也无法演示
但是应是如此
三、&(取地址) 与 const&重载
const
修饰的取地址重载函数绝大多数情况是不需要手动实现
的,编译器自动生成的就已经能够解决绝大多数的情况
除非,不想取地址成功这两个默认成员函数,都是在取对象地址时自动调用的.
作者: 哈米d1ch 发表日期:2022 年 6 月 26 日