Ch 22: 运算符重载
- 深入理解运算符重载的概念和设计原则
- 掌握成员运算符和友元运算符的选择
- 学会实现各种常见运算符(算术、比较、下标等)
- 理解 C++20 三路比较运算符
<=> - 掌握类型转换运算符的使用
22.1 Python 运算符重载回顾
Section titled “22.1 Python 运算符重载回顾”Python 的 __add__ 等方法
Section titled “Python 的 __add__ 等方法”# Python - 通过特殊方法重载运算符class Vec2: def __init__(self, x, y): self.x = x self.y = y
def __add__(self, other): return Vec2(self.x + other.x, self.y + other.y)
def __repr__(self): return f"Vec2({self.x}, {self.y})"
def __eq__(self, other): return self.x == other.x and self.y == other.y
def __mul__(self, scalar): return Vec2(self.x * scalar, self.y * scalar)
def __rmul__(self, scalar): return Vec2(self.x * scalar, self.y * scalar)
v1 = Vec2(1, 2)v2 = Vec2(3, 4)print(v1 + v2) # Vec2(4, 6)print(v1 * 3) # Vec2(3, 6)print(2 * v1) # Vec2(2, 4) - 调用 __rmul__print(v1 == Vec2(1, 2)) # TruePython 的其他运算符方法
Section titled “Python 的其他运算符方法”# 常用运算符方法__add__ # +__sub__ # -__mul__ # *__truediv__ # /__floordiv__ # //__mod__ # %__pow__ # **__and__ # &__or__ # |__xor__ # ^__lt__ # <__le__ # <=__gt__ # >__ge__ # >=__eq__ # ==__ne__ # !=__getitem__ # []__setitem__ # [] =__call__ # ()__len__ # len()__iter__ # iter()22.2 C++ 运算符重载基础
Section titled “22.2 C++ 运算符重载基础”#include <iostream>#include <string>
class Vec2 {public: Vec2(double x, double y) : x_(x), y_(y) {}
// 成员运算符:+= Vec2& operator+=(const Vec2& other) { x_ += other.x_; y_ += other.y_; return *this; }
// 成员运算符:+ Vec2 operator+(const Vec2& other) const { return Vec2(x_ + other.x_, y_ + other.y_); }
// 成员运算符:- * Vec2 operator-(const Vec2& other) const { return Vec2(x_ - other.x_, y_ - other.y_); }
Vec2 operator*(double scalar) const { return Vec2(x_ * scalar, y_ * scalar); }
// 比较运算符 bool operator==(const Vec2& other) const { return x_ == other.x_ && y_ == other.y_; }
bool operator!=(const Vec2& other) const { return !(*this == other); }
// 友元声明 - 用于 << 运算符 friend std::ostream& operator<<(std::ostream& os, const Vec2& v);
private: double x_; double y_;};
// 友元函数实现std::ostream& operator<<(std::ostream& os, const Vec2& v) { return os << "Vec2(" << v.x_ << ", " << v.y_ << ")";}
// 非成员运算符:scalar * Vec2Vec2 operator*(double scalar, const Vec2& v) { return v * scalar; // 利用已有的成员运算符}
int main() { Vec2 v1(1, 2), v2(3, 4);
std::cout << "v1 = " << v1 << "\n"; // Vec2(1, 2) std::cout << "v2 = " << v2 << "\n"; // Vec2(3, 4) std::cout << "v1 + v2 = " << (v1 + v2) << "\n"; // Vec2(4, 6) std::cout << "v2 - v1 = " << (v2 - v1) << "\n"; // Vec2(2, 2) std::cout << "v1 * 3 = " << (v1 * 3) << "\n"; // Vec2(3, 6) std::cout << "2 * v2 = " << (2 * v2) << "\n"; // Vec2(6, 8) std::cout << "v1 == v1: " << std::boolalpha << (v1 == v1) << "\n"; std::cout << "v1 == v2: " << (v1 == v2) << "\n";
return 0;}成员运算符 vs 非成员运算符
Section titled “成员运算符 vs 非成员运算符”| 运算符类型 | 推荐方式 | 原因 |
|---|---|---|
a op b(两边同类) | 成员或友元 | 对称 |
a op b(左边不是此类) | 友元/非成员 | 需要左操作数 |
a += b | 成员 | 修改左操作数 |
a == b | 成员或友元 | 对称 |
// ❌ 错误:v1 + 5 可能编译,但 5 + v1 不行class Vec2 { Vec2 operator+(double scalar) { /* ... */ }};
// ✅ 正确:同时支持两种class Vec2 { Vec2 operator+(double scalar) const { /* ... */ }};friend Vec2 operator+(double, const Vec2&); // 左操作数不是此类
// 调用v1 + 5; // v1.operator+(5)5 + v1; // operator+(5, v1)22.3 算术运算符实现
Section titled “22.3 算术运算符实现”#include <iostream>#include <cmath>
class Complex {public: Complex(double real, double imag) : real_(real), imag_(imag) {}
// 一元负号 Complex operator-() const { return Complex(-real_, -imag_); }
// 一元正号(通常只是返回拷贝) Complex operator+() const { return *this; }
// 前置递增 Complex& operator++() { ++real_; return *this; }
// 后置递增(int 参数区分) Complex operator++(int) { Complex temp(*this); ++real_; return temp; }
double real() const { return real_; } double imag() const { return imag_; }
private: double real_; double imag_;};
int main() { Complex c(3, 4);
Complex neg = -c; // (-3, -4) Complex pos = +c; // (3, 4)
++c; // (4, 4) c++; // (5, 4)
return 0;}#include <string>
class String {public: explicit String(const std::string& s) : data_(s) {}
// 字符串连接 String operator+(const String& other) const { return String(data_ + other.data_); }
// 字符串比较 bool operator==(const String& other) const { return data_ == other.data_; }
bool operator<(const String& other) const { return data_ < other.data_; }
std::string get() const { return data_; }
private: std::string data_;};
// 支持 String + const char*String operator+(const String& lhs, const char* rhs) { return lhs + String(rhs);}
// 支持 const char* + StringString operator+(const char* lhs, const String& rhs) { return String(lhs) + rhs;}
int main() { String s1("Hello"); String s2(" World");
auto s3 = s1 + s2; // Hello World auto s4 = s1 + "!"; // Hello! auto s5 = "Hi, " + s1; // Hi, Hello
return 0;}复合赋值运算符
Section titled “复合赋值运算符”class Vec2 {public: Vec2& operator+=(const Vec2& other) { x_ += other.x_; y_ += other.y_; return *this; }
Vec2& operator-=(const Vec2& other) { x_ -= other.x_; y_ -= other.y_; return *this; }
Vec2& operator*=(double scalar) { x_ *= scalar; y_ *= scalar; return *this; }
Vec2& operator/=(double scalar) { x_ /= scalar; y_ /= scalar; return *this; }
// 基于 += 实现 + Vec2 operator+(const Vec2& other) const { Vec2 result(*this); result += other; return result; }
private: double x_, y_;};22.4 比较运算符
Section titled “22.4 比较运算符”#include <string>
class Person {public: Person(const std::string& name, int age) : name_(name), age_(age) {}
bool operator==(const Person& other) const { return name_ == other.name_ && age_ == other.age_; }
bool operator!=(const Person& other) const { return !(*this == other); }
bool operator<(const Person& other) const { return name_ < other.name_ || (name_ == other.name_ && age_ < other.age_); }
bool operator<=(const Person& other) const { return !(other < *this); }
bool operator>(const Person& other) const { return other < *this; }
bool operator>=(const Person& other) const { return !(*this < other); }
private: std::string name_; int age_;};C++20 三路比较运算符 <=>
Section titled “C++20 三路比较运算符 <=>”#include <compare>#include <iostream>
class Point {public: Point(int x, int y) : x_(x), y_(y) {}
// C++20:自动生成所有比较运算符 auto operator<=>(const Point& other) const = default;
// 或者自定义顺序 auto operator<=>(const Point& other) const { if (auto cmp = x_ <=> other.x_; cmp != 0) return cmp; return y_ <=> other.y_; }
private: int x_; int y_;};
int main() { Point p1(1, 2); Point p2(1, 2); Point p3(2, 1);
// C++20 比较结果 std::cout << std::boolalpha; std::cout << "p1 == p2: " << (p1 == p2) << "\n"; // true std::cout << "p1 < p3: " << (p1 < p3) << "\n"; // true std::cout << "p1 <= p2: " << (p1 <= p2) << "\n"; // true std::cout << "p1 > p3: " << (p1 > p3) << "\n"; // false
// spaceship 运算符返回 std::strong_ordering auto result = p1 <=> p3; if (result < 0) { std::cout << "p1 < p3\n"; } else if (result > 0) { std::cout << "p1 > p3\n"; } else { std::cout << "p1 == p3\n"; }
return 0;}部分排序(自定义比较)
Section titled “部分排序(自定义比较)”#include <compare>#include <string>#include <iostream>
class Person {public: Person(const std::string& name, int age) : name_(name), age_(age) {}
// 按年龄排序,但同年龄按姓名 auto operator<=>(const Person& other) const { if (auto cmp = age_ <=> other.age_; cmp != 0) return cmp; return name_ <=> other.name_; }
private: std::string name_; int age_;};
int main() { Person p1("Alice", 30); Person p2("Bob", 25); Person p3("Alice", 25);
std::cout << "p1 < p2: " << (p1 < p2) << "\n"; // true(Bob 更年轻) std::cout << "p2 < p3: " << (p2 < p3) << "\n"; // true(同名,Alice 更年轻) std::cout << "p1 > p2: " << (p1 > p2) << "\n"; // false
return 0;}22.5 下标运算符
Section titled “22.5 下标运算符”#include <vector>#include <stdexcept>
class Matrix {public: Matrix(std::size_t rows, std::size_t cols) : rows_(rows), cols_(cols), data_(rows * cols, 0.0) {}
// 下标访问 double& operator()(std::size_t row, std::size_t col) { check_bounds(row, col); return data_[row * cols_ + col]; }
const double& operator()(std::size_t row, std::size_t col) const { check_bounds(row, col); return data_[row * cols_ + col]; }
// 一维下标 double& operator[](std::size_t index) { return data_[index]; }
const double& operator[](std::size_t index) const { return data_[index]; }
std::size_t rows() const { return rows_; } std::size_t cols() const { return cols_; }
private: void check_bounds(std::size_t row, std::size_t col) const { if (row >= rows_ || col >= cols_) { throw std::out_of_range("Matrix index out of range"); } }
std::size_t rows_; std::size_t cols_; std::vector<double> data_;};
int main() { Matrix m(3, 3);
m(0, 0) = 1.0; m(1, 1) = 2.0; m(2, 2) = 3.0;
std::cout << "m(1,1) = " << m(1, 1) << "\n";
// [] 操作 m[0] = 9.0; // 设置第一个元素
return 0;}22.6 函数调用运算符
Section titled “22.6 函数调用运算符”#include <vector>#include <algorithm>#include <iostream>
// 函数对象(仿函数)class Multiply {public: explicit Multiply(int factor) : factor_(factor) {}
int operator()(int x) const { return x * factor_; }
private: int factor_;};
class IsEven {public: bool operator()(int x) const { return x % 2 == 0; }};
int main() { // 函数对象 Multiply by_3(3); Multiply by_5(5);
std::cout << by_3(10) << "\n"; // 30 std::cout << by_5(10) << "\n"; // 50
// 用于算法 std::vector<int> numbers = {1, 2, 3, 4, 5};
std::transform(numbers.begin(), numbers.end(), numbers.begin(), by_3); // numbers = {3, 6, 9, 12, 15}
int evens = std::count_if(numbers.begin(), numbers.end(), IsEven()); std::cout << "Evens: " << evens << "\n";
// Lambda 是函数对象的语法糖 auto add_10 = [](int x) { return x + 10; }; std::cout << add_10(5) << "\n"; // 15
return 0;}标准库的函数对象
Section titled “标准库的函数对象”#include <functional>#include <algorithm>#include <vector>#include <iostream>
int main() { std::vector<int> a = {1, 3, 5, 7}; std::vector<int> b = {2, 4, 6, 8};
// 预定义函数对象 std::vector<int> c(4);
// 加法 std::transform(a.begin(), a.end(), b.begin(), c.begin(), std::plus<int>()); // c = {3, 7, 11, 15}
// 乘法 std::transform(a.begin(), a.end(), b.begin(), c.begin(), std::multiplies<int>()); // c = {2, 12, 30, 56}
// 使用 std::bind 绑定参数 auto divide_by_2 = std::bind(std::divides<int>(), std::placeholders::_1, 2); std::vector<int> d = {1, 2, 3, 4, 5}; std::transform(d.begin(), d.end(), d.begin(), divide_by_2); // d = {0, 1, 1, 2, 2}
return 0;}22.7 类型转换运算符
Section titled “22.7 类型转换运算符”#include <string>#include <iostream>
class Fraction {public: Fraction(int num, int denom) : num_(num), denom_(denom) { normalize(); }
void normalize() { if (denom_ < 0) { num_ = -num_; denom_ = -denom_; } int g = gcd(std::abs(num_), std::abs(denom_)); num_ /= g; denom_ /= g; }
static int gcd(int a, int b) { while (b != 0) { int temp = b; b = a % b; a = temp; } return a; }
// 显式转换为 double explicit operator double() const { return static_cast<double>(num_) / denom_; }
// 隐式转换为 string(不推荐,但有时有用) operator std::string() const { return std::to_string(num_) + "/" + std::to_string(denom_); }
int num() const { return num_; } int denom() const { return denom_; }
private: int num_; int denom_;};
class BoolFlag {public: explicit BoolFlag(bool value) : value_(value) {}
// 转换为 bool explicit operator bool() const { return value_; }
private: bool value_;};
int main() { Fraction f(3, 4);
// 显式转换 double d = static_cast<double>(f); // 0.75 std::cout << "As double: " << d << "\n";
// 隐式转换(如果有非 explicit 的转换运算符) std::string s = f; // 调用 operator std::string() std::cout << "As string: " << s << "\n";
// BoolFlag 用于条件判断 BoolFlag flag(true); if (flag) { // 调用 operator bool() std::cout << "Flag is true\n"; }
// explicit 防止意外转换 // void process(double x); // process(f); // ❌ 编译错误!explicit 转换不能隐式调用
return 0;}22.8 输入输出运算符
Section titled “22.8 输入输出运算符”#include <iostream>#include <string>
class Person {public: Person() = default; Person(const std::string& name, int age) : name_(name), age_(age) {}
// 友元声明 friend std::istream& operator>>(std::istream& is, Person& p); friend std::ostream& operator<<(std::ostream& os, const Person& p);
private: std::string name_; int age_ = 0;};
std::ostream& operator<<(std::ostream& os, const Person& p) { return os << "Person{name=" << p.name_ << ", age=" << p.age_ << "}";}
std::istream& operator>>(std::istream& is, Person& p) { // 读取格式: "Name,Age" char comma; std::string name; int age;
is >> name >> comma >> age; if (is) { p.name_ = name; p.age_ = age; } return is;}
int main() { Person p1("Alice", 30); std::cout << p1 << "\n";
Person p2; std::cout << "Enter person (name,age): "; std::cin >> p2; std::cout << "You entered: " << p2 << "\n";
return 0;}22.9 完整示例:复数类
Section titled “22.9 完整示例:复数类”#include <iostream>#include <cmath>#include <compare>
class Complex {public: Complex(double real = 0, double imag = 0) : real_(real), imag_(imag) {}
// 访问 double real() const { return real_; } double imag() const { return imag_; }
// 算术运算符 Complex& operator+=(const Complex& other) { real_ += other.real_; imag_ += other.imag_; return *this; }
Complex& operator-=(const Complex& other) { real_ -= other.real_; imag_ -= other.imag_; return *this; }
Complex& operator*=(const Complex& other) { double new_real = real_ * other.real_ - imag_ * other.imag_; double new_imag = real_ * other.imag_ + imag_ * other.real_; real_ = new_real; imag_ = new_imag; return *this; }
Complex& operator/=(const Complex& other) { double denom = other.real_ * other.real_ + other.imag_ * other.imag_; double new_real = (real_ * other.real_ + imag_ * other.imag_) / denom; double new_imag = (imag_ * other.real_ - real_ * other.imag_) / denom; real_ = new_real; imag_ = new_imag; return *this; }
Complex operator+(const Complex& other) const { Complex result(*this); result += other; return result; }
Complex operator-(const Complex& other) const { Complex result(*this); result -= other; return result; }
Complex operator*(const Complex& other) const { Complex result(*this); result *= other; return result; }
Complex operator/(const Complex& other) const { Complex result(*this); result /= other; return result; }
// 一元运算符 Complex operator-() const { return Complex(-real_, -imag_); }
Complex operator+() const { return *this; }
// 比较运算符(C++20) auto operator<=>(const Complex& other) const { if (auto cmp = real_ <=> other.real_; cmp != 0) return cmp; return imag_ <=> other.imag_; }
bool operator==(const Complex& other) const = default;
// 转换 double magnitude() const { return std::sqrt(real_ * real_ + imag_ * imag_); }
double phase() const { return std::atan2(imag_, real_); }
// 输出 friend std::ostream& operator<<(std::ostream& os, const Complex& c) { os << c.real_; if (c.imag_ >= 0) os << "+"; os << c.imag_ << "i"; return os; }
private: double real_; double imag_;};
int main() { Complex a(3, 4); Complex b(1, -2);
std::cout << "a = " << a << "\n"; std::cout << "b = " << b << "\n"; std::cout << "a + b = " << (a + b) << "\n"; std::cout << "a - b = " << (a - b) << "\n"; std::cout << "a * b = " << (a * b) << "\n"; std::cout << "a / b = " << (a / b) << "\n"; std::cout << "-a = " << (-a) << "\n"; std::cout << "|a| = " << a.magnitude() << "\n"; std::cout << "arg(a) = " << a.phase() << "\n";
return 0;}22.10 章节总结
Section titled “22.10 章节总结”| 运算符类型 | 常用实现方式 |
|---|---|
算术 +, -, *, / | 成员函数,返回新对象 |
复合赋值 +=, -= 等 | 成员函数,返回 *this |
比较 ==, <, > | C++20 用 <=> 自动生成 |
下标 [] | 成员函数,返回引用 |
输入输出 <<, >> | 友元函数 |
| 类型转换 | operator T() 或 explicit operator T() |
函数调用 () | 成员函数(仿函数) |
设计原则:
- 运算符应符合语义(如
+不应修改左操作数) - 对称运算符用友元函数实现
- 优先实现
+=等复合赋值,再基于它实现+ - 用
explicit防止意外的类型转换 - C++20 优先使用
<=>自动生成比较运算符
下章预告:ch23 学习智能指针 unique_ptr、shared_ptr 和 weak_ptr。