c++ 运算符重载
一、什么是运算符重载?
运算符重载是 c++ 的一种特性,它允许程序员为自定义类型(如类和结构体)重新定义运算符的行为。通过运算符重载,我们可以使用熟悉的运算符语法来操作自定义类型的对象,从而使代码更加简洁、直观。
当运算符被⽤于类类型的对象时,c++语⾔允许我们通过运算符重载的形式指定新的含义。c++规定类类型对象使⽤运算符时,必须转换成调⽤对应运算符重载,若没有对应的运算符重载,则会编译报错。
例如,我们可以为自定义的复数类重载 +
运算符,使得两个复数对象可以直接使用 +
进行相加:
complex a(1, 2); complex b(3, 4); complex c = a + b; // 使用重载的 + 运算符
二、运算符重载的语法规则
在 c++ 中,运算符重载通过定义特殊的成员函数或非成员函数来实现。
其基本语法如下:
返回类型 operator运算符(参数列表) { // 函数体 }
其中:
operator
是 c++ 的关键字,用于声明一个运算符重载函数。- 运算符 是要重载的运算符,如
+
,-
,*
,/
等。 - 返回类型 是运算符重载函数的返回值类型,通常与操作数的类型相关。
- 参数列表 是运算符重载函数的参数,参数的个数和类型取决于运算符的种类和重载方式。
注意:
- ⼀个类需要重载哪些运算符,是看哪些运算符重载后有意义
- 重载++运算符时,有前置++和后置++,运算符重载函数名都是operator++,⽆法很好的区分。c++规定,后置++重载时,增加⼀个int形参,跟前置++构成函数重载,⽅便区分。
- 当运算符被⽤于类类型的对象时,c++语⾔允许我们通过运算符重载的形式指定新的含义。c++规定类类型对象使⽤运算符时,必须转换成调⽤对应运算符重载,若没有对应的运算符重载,则会编译报错。
- 运算符重载是具有特殊名字的函数,他的名字是由operator和后⾯要定义的运算符共同构成。和其他函数⼀样,它也具有其返回类型和参数列表以及函数体。
- 重载运算符函数的参数个数和该运算符作⽤的运算对象数量⼀样多。⼀元运算符有⼀个参数,⼆元运算符有两个参数,⼆元运算符的左侧运算对象传给第⼀个参数,右侧运算对象传给第⼆个参数。
- 如果⼀个重载运算符函数是成员函数,则它的第⼀个运算对象默认传给隐式的this指针,因此运算符重载作为成员函数时,参数⽐运算对象少⼀个。
- 运算符重载以后,其优先级和结合性与对应的内置类型运算符保持⼀致。
- 不能通过连接语法中没有的符号来创建新的操作符:⽐如operator@。
重载<<和>>时,需要重载为全局函数,因为重载为成员函数,this指针默认抢占了第⼀个形参位置,第⼀个形参位置是左侧运算对象,调⽤时就变成了 对象<<cout,不符合使⽤习惯和可读性。
重载为全局函数把ostream/istream放到第⼀个形参位置就可以了,第⼆个形参位置当类类型对象。
成员函数重载
运算符重载函数可以作为类的成员函数来定义。在这种情况下,函数的参数个数比运算符的操作数少一个,因为第一个操作数是通过 this
指针隐式传递的。
非成员函数重载
运算符重载函数也可以作为非成员函数(全局函数或友元函数)来定义。
在这种情况下,函数的参数个数与运算符的操作数相同。
赋值运算符重载
赋值运算符重载是⼀个默认成员函数,⽤于完成两个已经存在的对象直接的拷⻉赋值,这⾥要注意跟拷⻉构造区分,拷⻉构造⽤于⼀个对象拷⻉初始化给另⼀个要创建的对象。
赋值运算符重载的特点:
- 赋值运算符重载是⼀个运算符重载,规定必须重载为成员函数。赋值运算重载的参数建议写成const当前类类型引⽤,否则会传值传参会有拷⻉
- 有返回值,且建议写成当前类类型引⽤,引⽤返回可以提⾼效率,有返回值⽬的是为了⽀持连续赋值场景。
- 没有显式实现时,编译器会⾃动⽣成⼀个默认赋值运算符重载,默认赋值运算符重载⾏为跟默认拷⻉构造函数类似,对内置类型成员变量会完成值拷⻉/浅拷⻉(⼀个字节⼀个字节的拷⻉),对⾃定义类型成员变量会调⽤他的赋值重载函数。
拷贝构造:一个存在的对象去初始化另一个要实例化的对象; 赋值重载:已存在的两个对象之间的拷贝,注意返回值为类类型,处理连续赋值的情况
取地址运算符重载
const成员函数
- 将const修饰的成员函数称之为const成员函数,const修饰成员函数放到成员函数参数列表的后⾯。
- const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进⾏修改。date* const this 变为 const date* const this
取地址运算符重载
- 取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载,⼀般这两个函数编译器⾃动⽣成的就可以够我们⽤了,不需要去显⽰实现。
- 除⾮⼀些很特殊的场景,⽐如我们不想让别⼈取到当前类对象的地址,就可以⾃⼰实现⼀份,胡乱返回⼀个地址。
三、可重载的运算符和不可重载的运算符
可重载的运算符
c++ 中大部分运算符都可以被重载,包括:
- 算术运算符:
+
,-
,*
,/
,%
,++
,--
- 比较运算符:
==
,!=
,<
,>
,<=
,>=
- 逻辑运算符:
&&
,||
,!
- 位运算符:
&
,|
,^
,~
,<<
,>>
- 赋值运算符:
=
,+=
,-=
,*=
,/=
,%=
,&=
,|=
,^=
,<<=
,>>=
- 其他运算符:
[]
,()
,->
,,
,*
(解引用),&
(取地址)
不可重载的运算符
c++ 中少数运算符不能被重载,包括:
- 作用域解析运算符:
::
- 成员访问运算符:
.
和.*
- 条件运算符:
?:
- 类型转换运算符:
typeid
和dynamic_cast
等 - 编译时运算符:
sizeof
五、运算符重载的注意事项
在使用运算符重载时,需要注意以下几点:
- 遵循运算符的原有语义:运算符重载应该保持运算符原有的语义,避免造成混淆。例如,重载
+
运算符应该实现加法的语义,而不是减法。 - 不要滥用运算符重载:虽然运算符重载可以使代码更加简洁,但过度使用或滥用会使代码变得难以理解和维护。
- 保持运算符的优先级和结合性:运算符重载不能改变运算符的优先级和结合性,这是由语言规定的。
- 正确处理 const 对象:如果运算符重载函数不修改对象的状态,应该将其声明为
const
成员函数。 - 注意内存管理:在重载赋值运算符和拷贝构造函数时,需要特别注意深拷贝和浅拷贝的问题,避免内存泄漏。
- 避免创建新的运算符:c++ 不允许创建新的运算符,只能重载已有的运算符。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论