目录
概念
写法
约定俗成
注意事项
概念
其实早在刚开始学习C++的时候我们就已经接触到运算符重载了,只是我们当时还没意识到。
std::cout << "Hello World" << std::endl;
对于这一句代码的解释如下:
cout其实是一个iostream类的对象,它重载了左移运算符(operator<< ),每次调用的时候就会向输出设备(一般就是控制台)输出东西。也就是说<<原本是一个左移运算符,但通过所谓的“重载操作”之后就可以执行输出操作了。
而至于运算符重载是什么呢?
运算符重载是一种C++编程特性,它允许程序员重新定义已有的运算符的行为,使得自定义的数据类型(类)可以像内置类型一样使用这些运算符。通过运算符重载,可以让自定义类对象之间进行各种运算和操作,使代码更加直观、灵活和易于理解。
在C++中,运算符重载通过重载函数(也称为运算符函数)来实现,这些函数的名称和运算符相同,但在函数名前加上"operator"关键字,后面紧跟着运算符的符号。运算符重载函数可以定义为类的成员函数或全局函数,取决于运算符重载的需求和设计。
写法
接下来就让我们继续探讨C++运算符重载的写法。写法就是将函数设置为如下格式:
返回值 operator运算符( 参数… );
下面是代码示例模板:
class MyClass {
public:
// 构造函数(如果需要)
MyClass(/* 参数列表 */)
{
// 在这里初始化对象的成员变量
}
// 一元运算符重载
// 例如:一元取反运算符(-),一元递增运算符(++)
// 返回类型 operator运算符() {
// 在这里定义运算符的操作
// }
// 二元运算符重载
// 例如:二元加法运算符(+),二元乘法运算符(*)
// 返回类型 operator运算符(const MyClass& other) {
// 在这里定义运算符的操作
// }
// 赋值运算符重载
// 返回类型 operator=(const MyClass& other) {
// 在这里定义运算符的操作
// }
// 其他常见运算符重载
// 例如:等于运算符(==),不等于运算符(!=),小于运算符(<),大于运算符(>)
// 返回类型 operator运算符(const MyClass& other) {
// 在这里定义运算符的操作
// }
// 友元函数(如果需要)
// 例如:输出运算符(<<),输入运算符(>>)
// friend 返回类型 operator运算符(std::ostream& os, const MyClass& obj) {
// 在这里定义运算符的操作
// return os;
// }
};
// 请注意,如果运算符是成员函数,它至少有一个操作数(隐含的 this 指针),
// 而如果运算符是全局函数(通过友元实现),它也许会有两个操作数。
// 如果运算符重载作为成员函数,使用对象的方式调用:
// MyClass obj1, obj2, result;
// result = obj1.operator+(obj2); // 调用二元加法运算符重载
// 如果运算符重载作为全局函数(通过友元实现),使用函数的方式调用:
// MyClass obj1, obj2, result;
// result = operator+(obj1, obj2); // 调用二元加法运算符重载
如果不知道哪些运算符可以重载,哪些运算符不能重载,以及对于一个陌生的运算符重载函数该如何书写还感到疑惑,可以尝试看这篇博客:C++中一些常见的运算符重载代码模板
约定俗成
在C++中,一般来说,大多数运算符的重载既可以在类内声明,也可以在类外声明。然而,有一些约定俗成的实践和限制,决定了哪些运算符更适合在类内重载,哪些更适合在类外重载,以及哪些运算符在类内外重载无所谓。大致内容见下图:
具体细节如下:
类内重载
- 单目运算符:单目运算符只涉及一个操作数,比如自增(++)和自减(--)运算符。这类运算符通常可以在类内进行重载,因为它们的操作数是对象本身。
- 赋值运算符:赋值运算符通常在类内进行重载,用于对类的成员变量进行赋值操作。这样可以很方便地访问类的私有成员。
- 下标运算符:下标运算符用于对象进行类似于数组的访问,通常也在类内重载。
- 函数调用运算符:函数调用运算符(就是一对小括号)一般也在类内重载,用于使类的对象可以像函数一样被调用。
类外重载
- 双目运算符:双目运算符涉及两个操作数的运算,例如加法(+)、减法(-)和乘法(*)等。对于双目运算符,尤其是涉及非成员类型的运算符,更倾向于在类外重载。这样可以保持对称性,允许两个不同类型的对象之间进行运算。
- 流插入和流提取:流插入运算符(<<)和流提取运算符(>>)通常在类外部进行重载。这样使得能够以自定义的方式进行输入和输出操作,而不需要改变类的定义。
- 关系运算符:关系运算符(如<、>、<=、>=、==、!=)通常在类外部重载,特别是涉及两个不同类对象之间的比较。
类内类外无所谓
算术运算符:加法、减法、乘法、除法等算术运算符既可以在类内重载,也可以在类外重载,这取决于对类的设计和需求。如果运算符只涉及一个操作数(一元运算符),通常在类内重载。如果涉及两个操作数(二元运算符),则可以在类内或类外重载。
注意事项
下面是使用C++运算符重载时的一些注意事项:
- C++中不允许用户定义新的运算符,只能对已有的运算符进行重载。
- 重载后的运算符的优先级、结合性也要保持不变,也不能改变其操作数及语法结构。
- 重载后的含义与原运算符的含义要保持一致。例如+重载之后也应为加的含义。
- 运算符重载函数不能有默认参数,否则就意味着改变了运算符操作数的个数。
- 运算符重载既可以在类内定义,也可以在类外全局下定义。
- 运算符重载函数至少要有一个自定义类型的参数,因为如果都是内置类型的话,运算符重载也就没有意义了。
- 当运算符重载函数在类内声明时,其形参看起来比操作数数目少1,这是因为函数的第一个参数为隐式的this指针。
- 建议不要重载operator&& 和 operatorll。原因是无法在这两种情况下实现内置操作符的完整语义。说得更具体一些,内置版本版本特殊之处在于:内置版本的 && 和 II 有很灵活的短路规则,而我们自定义的运算符重载无法实现 && 和 || 的短路规则,与长久以来的习惯冲突,所以最好不要重载 && 和 ||。
- 点运算符(.)、域运算符(::)、点星运算符 (.*)、条件运算符( ? : )以及sizeof运算符,这5个运算符不能发生重载。