Skip to content

14.1 - 运算符重载简介

Key Takeaway

在课程8.9 - 函数重载中,我们介绍了函数重载,它提供了一种机制,可以使用相同的名称创建和解析多个函数调用,只要每个函数都具有唯一的函数原型即可。这样,你就可以创建与不同数据类型一起使用的函数变体,而无需为每个变体想出一个独特的名称。

在C++中,操作符也被实现为函数。通过在操作符函数上使用函数重载,你可以自定义操作符的功能,使其可以与不同的数据类型一起使用(包括你编写的类)。使用函数重载来重载操作符称为操作符重载

在本章中,我们将检查与操作符重载相关的主题。

操作符作为函数

考虑以下示例:

1
2
3
int x { 2 };
int y { 3 };
std::cout << x + y << '\\\\\\\\n';

编译器提供了用于整数操作数的加号运算符(+)的内置版本 —— 该函数将整数xy相加,并返回一个整数结果。当你看到表达式x + y时,你可以将其在头脑中翻译为函数调用operator+(x, y)(其中operator+是函数的名称)。

考虑下面代码:

1
2
3
double z { 2.0 };
double w { 3.0 };
std::cout << w + z << '\\\\\\\\n';

编译器也附带了双精度操作数的加号运算符(+)的内置版本。表达式w + z变为函数调用operator+(w, z),并使用函数重载来确定编译器应该调用此函数的双精度版本而不是整数版本。

现在考虑如果我们尝试将两个用户定义的类的对象相加时会发生什么:

1
2
3
Mystring string1 { "Hello, " };
Mystring string2 { "World!" };
std::cout << string1 + string2 << '\\\\\\\\n';

运行结果如何?字符串“Hello,World!”本应该将会被打印到屏幕上。但是,由于Mystring是用户定义的类,编译器没有可用于Mystring类型操作数的加号运算符。因此,在这种情况下,编译器会报告错误。为了使其按照我们想要的方式工作,则需要重载函数——告诉编译器如何将+操作符与两个Mystring类型的操作数一起使用。我们将在下一课中看到如何做到这一点。

解析重载操作符

在计算包含操作符的表达式时,编译器使用以下规则:

  • 如果所有操作数都是基本数据类型,则编译器将调用内置例程(如果存在)。如果不存在,编译器将生成编译器错误。
  • 如果任何操作数是用户数据类型(例如你的类之一或枚举类型),则编译器将查看该类型是否具有匹配的重载操作符函数可以调用。如果找不到,它将尝试将一个或多个用户定义类型操作数转换为基本数据类型,以便它可以使用匹配的内置操作符(通过重载型转换,我们将在本章的后面介绍)。如果失败,则它将生成编译错误。

操作符重载的限制是什么?

首先,C++中几乎可以重载所有现有操作符。例外情况是:条件(?:),sizeof,范围(::),成员选择器(.),指针成员选择器(->),typeid以及转换操作符。

其次,你只能重载现有的操作符。你不能创建新操作符或重命名现有操作符。例如,你不能创建一个操作符**来执行幂。

第三,重载操作符中至少有一个操作数必须是用户定义类型。这意味着你不能重载加号操

第四,不可能改变操作符支持的操作数的数量。

最后,所有操作符都保持其默认的优先级和结合性(无论它们用于什么),这是不能改变的。

一些新的程序员试图重载位运算XOR操作符(^)来实现指数运算。但是,在C++中,operator^的优先级低于基本算术运算符,这会导致表达式评估不正确。

在基本数学中,指数运算优先于基本算术运算,因此4 + 3 ^ 2解析为4 + (3 ^ 2) => 4 + 9 => 13。但是,在C++中,算术运算符的优先级高于operator^,因此4 + 3 ^ 2解析为(4 + 3)^ 2 => 7 ^ 2 => 49

要正确使用它,每次都显式地为指数部分添加括号(例如4 + (3 ^ 2)),这并不直观,并且可能存在错误。

因此,出于这个优先级问题,最好只以类似于其原始意图的方式使用运算符。

最佳实践

当重载操作符时,最好使操作符的功能尽可能接近操作符的原始意图。

此外,由于操作符没有描述性的名称,它们的用途并不总是很清楚。例如,operator+ 可能是字符串类连接字符串的合理选择。但是operator-呢?你期望它做什么?这不清楚。

最佳实践

如果重载操作符的含义不清晰、不直观,请使用命名函数代替操作符重载。

在这些限制内,你仍然可以找到许多有用的功能来重载你的自定义类!你可以重载+运算符来连接你的用户定义的字符串类,或将两个Fraction类对象相加。你可以重载<<运算符,以便轻松将你的类打印到屏幕(或文件)。你可以重载等式运算符(==)来比较两个类对象。这使得运算符重载成为C ++中最有用的功能之一 —— 因为它允许你以更直观的方式使用你的类。

在接下来的课程中,我们将更深入地研究重载不同类型的运算符。