Skip to content

基本概念

运算符重载,就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。(运算符重载不能改变本来寓意,不能改变基础类型寓意)

在C++中,可以定义一个处理类的新运算符。这种定义很像一个普通的函数定义,只是函数的名字由关键字operator及其紧跟的运算符组成。差别仅此而已。它像任何其他函数一样也是一个函数,当编译器遇到适当的模式时,就会调用这个函数。

重载运算符是具有特殊名字的函数:它们的名字由关键字operator和其后要重载的运算符共同组成。和其他函数一样,重载运算符的函数也包括返回类型、参数列表及函数体,其语法格式如下所示:

cpp
返回类型 operator 运算符 (参数列表){
    函数体;
}

定义重载的运算符就像定义函数,只是该函数的名字是 operator@,这里的@代表了被重载的运算符。函数的参数中参数个数取决于两个因素。

  • 运算符是一元(一个参数)还是二元(两个参数);
  • 运算符被定义为全局函数(对于一元是一个参数,对于二元是两个参数)还是成员函数(对于一元没有参数,对于二元是一个参数——此时该类的对象用作引用参数)

[两个极端]

有些人很容易滥用运算符重载。它确实是一个有趣的工具,但是应该注意,它仅仅是一种语法上的方便而已,是另外一种函数调用的方式。从这个角度来看,只有在能使涉及类的代码更易写,尤其是更易读时(请记住,读代码的机会比我们写代码多多了)才有理由重载运算符。如果不是这样,就改用其他更易用、更易读的方式。 对于运算符重载,另外一个常见的反应是恐慌:突然之间,C 运算符的含义变得不同寻常了,一切都变了,所有 C 代码的功能都要改变!并非如此,。(例如想重载 int 类型数据的+号)

运算符重载规则

C++中并不是任意的运算符都可以重载,在重载运算符时也需要遵循一定的规则,具体如下:

  • 只能重载C++中已有的运算符,不能创建新的运算符
  • 重载之后的运算符不能改变其优先级和结合性,也不能改变其操作数的个数及语法结构
  • 运算符重载是针对新类型数据的实际需要,对原由运算符进行的适当改造,重载的功能应当与原有功能相类似,避免没有目的地使用重载运算符
  • C++中有5个运算符不能重载:类属关系运算符.、成员指针运算符*、作用域运算符::sizeof运算符和三目运算符?:

可重载的运算符

几乎C中所有的运算符都可以重载,但运算符重载的使用时相当受限制的。特别是不能使用C中当前没有意义的运算符(例如用**求幂)不能改变运算符优先级不能改变运算符的参数个数。这样的限制有意义,否则,所有这些行为产生的运算符只会混淆而不是澄清寓语意。

特殊运算符

  • =[]()-> 操作符只能通过成员函数进行重载
  • <<>> 操作符最好通过友元函数进行重载
  • 不要重载 &&|| 操作符,因为无法实现短路规则

常规建议

不可重载的运算符

不能重载operator&&operator|| 的原因是,无法在这两种情况下实现内置操作符的完整语义。说得更具体一些,内置版本版本特殊之处在于:内置版本的&&||首先计算左边的表达式,如果这完全能够决定结果,就无需计算右边的表达式了--而且能够保证不需要。我们都已经习惯这种方便的特性了。

我们说操作符重载其实是另一种形式的函数调用而已,对于函数调用总是在函数执行之前对所有参数进行求值。

示例
cpp
#include <iostream>
using namespace std;
#include <string>

class Complex {
public:
    Complex(int flag) {
        this->flag = flag;
    }
    Complex& operator+=(Complex& complex) {
        this->flag = this->flag + complex.flag;
        return *this;
    }
    bool operator&&(Complex& complex) {
        return this->flag && complex.flag;
    }

public:
    int flag;
};

int main(int argc, char *argv[]) {

    Complex complex1(0);//flag 0
    Complex complex2(1);//flag 1

    //原来情况,应该从左往右运算,左边为假,则退出运算,结果为假
    //这边却是,先运算(complex1+complex2),导致,complex1的flag变为complex1+complex2的值, complex1.a = 1
    // 1 && 1
    //complex1.operator&&(complex1.operator+=(complex2))
    if (complex1 && (complex1 += complex2)) {
        cout << "真!" << endl;
    }
    else {
        cout << "假!" << endl;
    }

    return 0;
}

程序输出:

shell
真!

根据内置&&的执行顺序,我们发现这个案例中执行顺序并不是从左向右,而是先右后左,这就是不满足我们习惯的特性了。由于complex1 += complex2先执行,导致 complex1 本身发生了变化,初始值是0,现在经过+=运算变成11 && 1输出了真。