运算符重载
前言
重载不陌生
1 | int add(int a){ |
函数重载就是一种。
正文
运算符重载就是利用常见的运算符+-*/
去做一些重载的事情。
比如string就可以实现+=
1 | string str = "hello "; |
默认的+肯定是只能对数值类型进行数学运算,而string这种重新设计的+就是运算符重载
语法:返回值类型 operator运算符();
自定义一个类
1 | class Box{ |
在正常情况下,比较两个实例化对象的height属性
1 | Box b1(100); |
按照常规逻辑,就是写一个函数
1 | bool toBig(Box &ba, Box &bb){ |
然后if在去判断这个返回值。
非成员函数运算符重载
那么如果有了运算符重载。
1 | bool operator<(Box &ba, Box &bb){ |
但是,这样在形式上看着可能跟函数换汤不换药的感觉。
在实际用的时候
1 | if (b1 < b2) std::cout << "b2 big" << std::endl; |
本质上是if (operator<(b1, b2)) std::cout << "b2 big" << std::endl;
既然是运算符重载,自然会转换成上面那种形式,因为有了模板,他就知道放两个类的时候,用什么比较。
如果变量是私有的,普通情况不具备访问权限,就可能要用friend友元操作,但是不是特别好
既然是模板,除了类和类,也可以用其他类型
1 | bool operator<(Box &ba, int x){ |
只不过像short float double那些还要额外重载。
成员函数运算符重载
上述的非成员就是不定义在类中,定义在类中的也好写
1 | class Box{ |
可以看到这个运算符重载是写在类中的。是属于类的成员。
与上述的本质也有差异if (b1.operator<(b2)) std::cout << "b2 big" << std::endl;
因为运算符重载通过函数模式发生,所以谁调用的就跟谁连在一起
为什么会有重载运算符
- 最主要就是让类也支持原生的运算
- 提示对程序的控制权,比如重载new delete new[] delete[]
但是注意,运算符的重载只是为了让代码更方便使用和维护,并不能提升什么开发效率
运算符重载的限制
- 只能通过现有运算符,不能自定义
- 有些运算符不能重载
- 对象访问
.
,比如user.hp - 作用域解析
::
,std::cout - 求大小运算符sizeof
- 三目运算
?:
- 对象访问
- 无法修改运算符本身优先级
- 在c++17之后,也不能修改运算符的操作数计算顺序,17之前 编译器可以自由选择,但是仍然是一种未定义行为
- 除了delete/delete[]和new/new[]外,不能对原生数据类型的其他运算符重载,比如char的+定义为-
- 除了new和delete以外,其他运算符的操作数的个数一律不能修改
不建议重载逻辑运算符,因为逻辑运算符重载之后将不会进行短路测试
c++17后计算的顺序规定为先计算左边在计算右边
运算符重载的正确姿势
1 | // 二元运算符重载 |
结语
运算符的重载借用关键字operator + 运算符符号,本质上还是跟函数差不多,只不过标准化之后优化了用起来比普通函数好很多。
然后重载的时候注意下合法问题,不要擅自做一些感觉牛皮的行为。