前言
本文要点:
- 什么是运算符重载
- 为什么要重载运算符
- 哪些运算符不可以重载
- 哪些运算符不建议重载
- 应该遵循哪些原则
- 如何重载运算符
什么是运算符重载
如果还不知道什么是重载,可以参考《什么是函数重载》和《彻底理清重载函数匹配》。
众所周知,运算符可直接作用于内置类型。例如,+可以用于将整型或者浮点型相加,如果是自定义的两个对象,你却不能直接进行相加,不过你可以通过重载运算符赋予这个运算符新的定义,
说白了,原先的很多运算符只能作用于内置类型,通过重载就可以赋予它们更多的含义,让它们可以作用于对象。
为什么要重载运算符
前面也已经说了,操作符的重载可以让运算符作用于类类型的对象,而对于有些作用于对象的运算符,也可以在不改变含义的情况下自定义操作,那么为什么要这么做呢?很显然,它能非常方便地操作对象又不能保持其操作的含义明确。
重载运算符让运算符有新的语义,但绝对不是改变它的语法。
哪些运算符不可以重载
可重载地运算符很多,所以这里列出不能被重载的运算符:1
:: .* . ? :
简单解释一下有何不妥:
- :: .* . 这三者的后部分作用于变量名,而不是某个具体值,仅访问而非操作,重载后语义将改变
- ?: 对于表达式exp0?exp1:exp2,重载后是执行exp1还是exp2,还是都执行?是不是和它的本意有差别?
以上两点非准确说法,仅供参考,欢迎提供更多思路。
如何重载
运算符重载函数的函数名由operator后面跟着要重载的运算符组成。例如,我们有一个对象Water,要重载+运算符:1
2
3
4
5
6Water& operator+(Water &b1,Water &b2)
{
/*do something
假设是将水的重量相加
*/
}
后面我们就可以直接这样使用啦:1
2
3Water b1;
Water b2;
b1 + b2;
这里b1+b2相当于下面的调用:1
operator+(b1,b2);
在运算符重载里面,要特别提一下++,即自增运算符,我们都知道自增运算符有前置和后置,但是它们作用的对象数量都是一个,该怎么区分呢?
为了区分这两种情况,C++为后置版本增加了一个额外的int类型参数。1
2
3
4
5
6
7
8
9
10//后置版本
Water Water::operator++(int)
{
/*do something*/
}
//前置版本
Water& Water::operator++()
{
/*do something*/
}
来源:公众号【编程珠玑】,网址:https://www.yanbinghu.com
应该遵循哪些原则
重载的运算符本质来说是特殊的函数,因此它的参数数量应该和运算符作用的对象一样多,也就是说,一元运算符有一个参数,二元运算符有两个参数。
除此之外,最基本的要求是,重载的含义应该与运算符作用于内置类型时一样的含义。例如说,你不应该重载一个+,用于两个对象的的比较或者相减。另外也重载也能非常明显地体现其含义,比如前面例子中的Water的相加,就显得不是特别好,Water相加是什么意思?
还有就是不能重定义内置运算符,看下面的例子:1
2
3
4
5
6
7
8
9
10
11
12
int operator+(int a,int b)
{
return a *2 + b;
}
int main(void)
{
int a = 1;
int b = 2;
int c = a + b;
return 0;
}
编译将会报错:1
2
3main.cpp:2:26: error: ‘int operator+(int, int)’ must have an argument of class or enumerated type
int operator+(int a,int b)
^
提示说,其参数类型必须是类或者枚举类型。
实际上,一个运算符函数至少含有一个类类型或者枚举类型的参数
哪些不建议重载
由于重载的运算符本质是函数,因此对于那些对作用对象求值顺序有要求的运算符应该尽量避免重载,例如逻辑运算符,逗号运算符等。
举例来说,||和&&具有短路属性:1
a || b || c
我们都知道,当表达式a为true时,b和c将不会被求值,
但是如果重载了||,并将它作用于重载对象,那它将不会有此性质,而是三个表达式都会求值。因此如果想要重载后的运算符也有短路性质,你是很难期望了。
注意,这里不建议并非不能重载。
总结
篇幅有限,本文不对具体的运算符重载进行详细介绍,但至少对运算符的重载有基本的概念,了解之后,再去了解更多的特定运算符重载原则。
最后:不要滥用运算符重载。