运算符重载及示例 (operator overloading)

运算符重载及示例 (operator overloading)#

假设我们定义了数学上的复数类, 则很可能需要复数类的加法等运算:

 1class Complex {
 2 public:
 3  Complex(int real, int imaginary);
 4  Complex(Complex const&);
 5
 6  Complex& add_by(Complex const& other) {
 7    real_      += other.real_;
 8    imaginary_ += other.imaginary_;
 9    return *this;
10  }
11  friend Complex add(Complex lhs, Complex rhs) {
12    Complex result(lhs);
13    result.add_by(rhs);
14    return result;
15  }
16
17 private:
18  int real_;
19  int imaginary_;
20};
1Complex lhs(1, 5);
2Complex rhs(2, 3);
3add(add(lhs, lhs), add(lhs, rhs));  // 😱

这太恶心了, 谁会有耐心去读 add(add(lhs, lhs), add(lhs, rhs)); 到底做了啥呢?

但是 int 不是这样的:

1int lhs = 1;
2int rhs = 3;
3(lhs + lhs) + (lhs + rhs);

我们可以让复数类也这样吗? C++ 的一个目标是 让用户自定义类型和内置类型拥有同等程度的支持, 自然提供了让用户自定义类型支持运算符的方法, 这称为运算符重载 (operator overloading).

很简单, 将 add_by 改为 operator+=, 将 add 改为 operator+:

 1class Complex {
 2 public:
 3  Complex(int real, int imaginary);
 4  Complex(Complex const&);
 5
 6  Complex& operator+=(Complex const& other) {
 7    real_      += other.real_;
 8    imaginary_ += other.imaginary_;
 9    return *this;
10  }
11  friend Complex operator+(Complex lhs, Complex rhs) {
12    Complex result(lhs);
13    result += rhs;
14    return result;
15  }
16
17 private:
18  int real_;
19  int imaginary_;
20};
1Complex lhs(1, 5);
2Complex rhs(2, 3);
3(lhs + lhs) + (lhs + rhs);  // 😋

这就是运算符重载: 函数名字是对应的运算符, 函数的参数个数是对应的参数个数, 并且参数中有自定义类型.

第一节课里的运算符重载#

我们在最开始就见过运算符重载:

1int value = 0;
2std::cin  >> value;
3std::cout << value;

<<>> 原本是位运算的移位运算符 (如 5 << 1), 但由于它形状上是箭头, 标准库将它用于输入输出, 这可以对应于:

1std::istream& operator>>(std::istream& istream, int& value) {
2  /* 从 istream 中读入值到 value */
3  return istream;
4}
5
6std::ostream& operator<<(std::ostream& ostream, int value) {
7  /* 将 value 值输出到 ostream */
8  return ostream;
9}

最佳实践#

运算符重载的方式可能基于应用, 你完全可以用运算符重载在 C++ 代码内部创造一种新的语言.

如果你目的是让自定义类型模拟内置类型 (如 int) 的行为, 则可以参考以下要求:

函数类型
  • 对于 = (赋值)、[] (下标访问)、() (函数调用) 和 -> (成员访问), 总是实现为 成员函数, 因为它们只能实现为成员函数.

  • 对于 一元运算符, 实现为 成员函数.

  • 对于 二元运算符:

    • 如果 它不对等地对待它的两个参数 (通常会改变左侧实参的内容, 并且需要访问私用成员), 则实现为其 左参数的成员函数.

    • 如果 它对等地对待它的两个参数, 实现为 友元函数并在类内定义 (hidden friend 惯用法).

函数参数和返回值

同样地, 模拟内置类型的行为, 具体参考以下示例.

此外, 如果参数不会被修改, 应该用 const& 而不是 &, 求求你这么做吧😭😭😭, 教材不这么做是因为它是垃圾教材.

class Widget 各种运算符模拟内置类型的实现示例 (C++98)

相关解答#