运算符重载及示例 (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)