引子:
在赋值运算符重载之前,先要知道什么是运算符重载。
一,运算符重载:
1.定义:
将已有的运算符重载为对对象操作具有一定含义的运算符
2.注意:
1.运算符重载的参数必须含有至少一个对象
2.根据原有运算符为二目/单目运算符决定重载参数只能是2/1个
3.作为类的成员函数重载时,参数默认会添一个this指针,注意参数数量
4.运算符必须是语言已有的,不能自己创造
5.有的运算符不能重载,如:(.*),(.),(::),(?:),(sizeof).
(sizeof也是运算符,或者说关键字)
6.重载哪些运算符,返回值是什么看具体需求
3.用法:
返回值 operator运算符(参数....)
{代码块}
4.理解:
1.本质:运算符重载本质是调用函数
2.外部使用本质:运算符操作对象当作实参按序传给函数
如:d1>d2本质是d1.operator>(d2);(左传对象,会转为指针传参this指针)
5.调用:
1.显示调用
像调用一般函数的格式调用。
2.转换调用
像正常运算符使用一样格式写。
6.重载位置:
1.在全局。
缺陷:
不能直接访问对象私有成员,只能通过getter或友元。
2.在类中。(建议)
缺陷:
参数会多个this指针。注意写的参数个数应-1。
优点:
能直接访问同类所有对象的成员。
(类的访问权限是类的所有对象->类的成员函数可以访问同类其他对象的私有成员)
7.参数:
要求:必须有个自定义类型对象,其他可为任意类型,内置类型,自定义类型都可。
注意传参顺序。
二,赋值重载:
1.定义:
运算符重载的一种,目的是对=赋值运算符重载,让已经初始化的对象间能相互拷贝。
2.区分赋值与拷贝构造:
1.使用场景:
拷贝构造是用已经初始化的对象对未初始化的对象进行拷贝初始化,赋值的两个对象都已经初始化了。
2.易混点:
拷贝构造有两种写法:
1.带括号。data d2(d1);
2.赋值符号。data d2=d1;由于未初始化,编译器会调拷贝构造
赋值:
data d2;
d2=d1;对象都已经初始化了,编译器调拷贝构造。
3.位置:
只能作为成员函数声明在类定义的内部(默认成员函数都是如此)
4.用法:
对象类型& operator=(另一个对象别名){代码块 return *this;}
解释:
1.为什么要返回对象类型
一般赋值重载默认返回左边对象,使其可以连续赋值。如:d1=d2=d3;d2返回给左边赋值运算符作为第二个参数。(功能完善)
2.为什么要返回别名
如果去掉引用,会将返回对象拷贝给临时变量,而不是直接传别名。(提高效率)
(图中红箭头全为拷贝)
3.参数1最好传别名,可以节省拷贝构造的成本。
(效率最高写法)
注意:
1.返回记得返回*this即当前对象。
2.最好加个if判断是否是自己拷贝自己,是就跳过。(防止深拷贝malloc的指针指向改变)
5.默认的赋值重载:
本质:
与默认拷贝构造一样,是浅拷贝,即按字节拷贝。
什么时候要自己写:
同析构函数,拷贝构造写的条件:类内部有这些资源:malloc,流,new,链接。
三,补充:
1.传值返回与引用返回区别
return d1;
1,传值返回:将d1拷贝构造给编译器的临时变量,再返回临时变量。
优点:不用考虑对象被销毁了。
缺点:效率低;
2,引用返回:语法上是返回对象的别名,对象的地址(底层)
优点:效率高
2.什么时候引用返回/传值返回
1.(优先考虑)引用返回:返回的变量/对象不会销毁(static,malloc.或不在此函数的栈的位置上,在外部函数中定义)。
2.能用引用返回就用引用返回,提高效率。
3.用引用接收/非引用接收的注意点
1.引用接收:
对传值返回的要用const引用接收:
因为编译器的临时变量具有常性(表达式和类型转换,返回都会用编译器的临时变量先接收)。
对引用返回的:
引用一个对象的别名,相当于引用这个对象(底层是指针互相传)
2.非引用接收
相当于拷贝返回的对象