8.4 运算符重载
运算符重载让自定义类型也能使用 +、==、<、[]、<< 等熟悉符号。这是 C++ 很有表现力的地方,但也需要克制。被重载的运算符应该符合读者对这个运算符的直觉。
例如,Fraction + Fraction 很清楚:得到一个新的和。Student + Student 通常就不清楚。好的 C++ 运算符让代码更短,但不应该让代码更神秘。
算术运算符
对于值类型,operator+ 通常返回一个新对象,并且不修改两个操作数。
cpp
class Fraction {
private:
int numerator_;
int denominator_;
public:
Fraction(int numerator, int denominator)
: numerator_(numerator), denominator_(denominator) {}
Fraction operator+(const Fraction& other) const {
int n = numerator_ * other.denominator_ + other.numerator_ * denominator_;
int d = denominator_ * other.denominator_;
return Fraction(n, d);
}
};左操作数是 *this,右操作数是 other。
cpp
Fraction half(1, 2);
Fraction third(1, 3);
Fraction sum = half + third; // 调用 half.operator+(third)函数后面的 const 表示 half + third 不会修改 half。如果你想写会修改对象的运算符,更适合使用 operator+=。
cpp
Fraction& operator+=(const Fraction& other) {
numerator_ = numerator_ * other.denominator_ + other.numerator_ * denominator_;
denominator_ = denominator_ * other.denominator_;
return *this;
}返回 *this 的引用,才能支持 a += b += c 这种链式写法。
正在加载交互实验...
正在加载概念检查...
正在加载本节练习...
流运算符
operator<< 通常写成非成员函数,因为它的左操作数是输出流,不是你的对象。
cpp
std::cout << value;编译器可以把它理解成:
cpp
operator<<(std::cout, value);如果需要访问 private 成员,流运算符通常会被声明成友元。
cpp
class Fraction {
private:
int numerator_;
int denominator_;
public:
Fraction(int numerator, int denominator)
: numerator_(numerator), denominator_(denominator) {}
friend std::ostream& operator<<(std::ostream& out, const Fraction& value);
};
std::ostream& operator<<(std::ostream& out, const Fraction& value) {
out << value.numerator_ << "/" << value.denominator_;
return out;
}返回 std::ostream& 非常重要。它把同一个输出流交还回去,让后面的 << 能继续链式输出:
cpp
std::cout << half << " + " << third << " = " << sum << std::endl;正在加载交互实验...
正在加载概念检查...
正在加载本节练习...
比较运算符
比较运算符应该回答清楚的问题。operator== 问两个值是否相等,operator< 问左边的值是否排在右边之前。
cpp
class Money {
private:
int cents_;
public:
explicit Money(int cents) : cents_(cents) {}
bool operator==(const Money& other) const {
return cents_ == other.cents_;
}
bool operator<(const Money& other) const {
return cents_ < other.cents_;
}
};这些函数都标记为 const,因为比较不应该修改对象。它们返回 bool,因为比较是在回答问题。一个类型拥有清晰的比较规则之后,排序、查找和测试都会更自然。
保持运算符含义不意外:
+不应该顺便打印。==不应该修改状态。<<应该写入输出流并返回输出流。- 如果某个运算符的含义很难用一句话解释,应该使用具名成员函数。
正在加载概念检查...
正在加载本节练习...