c++ 操作符重载详解与示例
-
-
- 操作符重载详解
-
- 一、基本规则
- 二、必须作为成员函数重载的运算符
-
- [1. 赋值运算符 `=`](#1. 赋值运算符
=
) - [2. 下标运算符 `[]`](#2. 下标运算符
[]
) - [3. 函数调用运算符 `()`](#3. 函数调用运算符
()
) - [4. 成员访问运算符 `->`](#4. 成员访问运算符
->
) - [5. 转型运算符](#5. 转型运算符)
- [1. 赋值运算符 `=`](#1. 赋值运算符
- 三、通常作为非成员函数重载的运算符
-
- [1. 算术运算符 `+`](#1. 算术运算符
+
) - [2. 输入/输出运算符 `<<` `>>`](#2. 输入/输出运算符
<<
>>
)
- [1. 算术运算符 `+`](#1. 算术运算符
- 四、特殊运算符
-
- [1. 自增/自减运算符](#1. 自增/自减运算符)
- [2. 比较运算符 `<=>`(C++20)](#2. 比较运算符
<=>
(C++20))
- 五、不建议重载的运算符
-
- [1. 逻辑运算符 `&&` `||`](#1. 逻辑运算符
&&
||
)
- [1. 逻辑运算符 `&&` `||`](#1. 逻辑运算符
- 六、运算符重载综合示例
- 七、不可重载的运算符
- 最佳实践原则:
-
操作符重载详解
操作符重载允许为自定义类型赋予与内置类型相似的操作行为,但需遵循特定规则。以下从基本规则、实现方式、具体运算符示例及注意事项展开讲解。
一、基本规则
-
使用
operator
关键字通过定义名为
operator@
的函数实现重载(@
表示运算符,如+
,==
等)。 -
不改变运算本质
- 不能发明新运算符(如
**
)。 - 不能改变运算符的优先级和结合性。
- 通常不改变运算符的直观含义(如
+
应实现加法而非减法)。
- 不能发明新运算符(如
-
参数要求
- 参数个数与操作数相同(如
+
需两个参数)。 - 至少一个参数为类类型(防止修改内置类型运算)。
- 参数个数与操作数相同(如
-
缺省参数限制
- 除
operator()
外,其他运算符不能有缺省参数。
- 除
二、必须作为成员函数重载的运算符
1. 赋值运算符 =
cpp
class MyString {
char* data;
public:
MyString& operator=(const MyString& other) {
if (this != &other) {
delete[] data;
data = new char[strlen(other.data)+1];
strcpy(data, other.data);
}
return *this;
}
};
2. 下标运算符 []
cpp
class Vector {
int arr[10];
public:
int& operator[](size_t index) { // 返回引用可修改
return arr[index];
}
const int& operator[](size_t index) const {
return arr[index];
}
};
3. 函数调用运算符 ()
cpp
class Adder {
public:
int operator()(int a, int b) const {
return a + b;
}
};
Adder add;
int sum = add(3, 5); // 输出8
4. 成员访问运算符 ->
cpp
class SmartPtr {
Data* ptr;
public:
Data* operator->() {
return ptr;
}
};
SmartPtr p;
p->func(); // 等价于(p.operator->())->func()
5. 转型运算符
cpp
class Rational {
int num, den;
public:
explicit operator double() const { // 禁止隐式转换
return static_cast<double>(num)/den;
}
};
Rational r(3,4);
double d = static_cast<double>(r);
三、通常作为非成员函数重载的运算符
1. 算术运算符 +
cpp
class Complex {
double real, imag;
public:
Complex(double r, double i) : real(r), imag(i) {}
friend Complex operator+(const Complex& a, const Complex& b);
};
Complex operator+(const Complex& a, const Complex& b) {
return {a.real+b.real, a.imag+b.imag};
}
2. 输入/输出运算符 <<
>>
cpp
class Date {
int y, m, d;
friend std::ostream& operator<<(std::ostream& os, const Date& dt);
friend std::istream& operator>>(std::istream& is, Date& dt);
};
std::ostream& operator<<(std::ostream& os, const Date& dt) {
return os << dt.y << '-' << dt.m << '-' << dt.d;
}
std::istream& operator>>(std::istream& is, Date& dt) {
return is >> dt.y >> dt.m >> dt.d;
}
四、特殊运算符
1. 自增/自减运算符
cpp
class Counter {
int value;
public:
Counter& operator++() { // 前置++
++value;
return *this;
}
Counter operator++(int) { // 后置++
Counter temp = *this;
++value;
return temp;
}
};
2. 比较运算符 <=>
(C++20)
cpp
class Point {
int x, y;
public:
auto operator<=>(const Point&) const = default;
};
// 自动生成 ==, !=, <, <=, >, >=
五、不建议重载的运算符
1. 逻辑运算符 &&
||
cpp
// 不推荐重载,因为会失去短路特性
class BoolWrapper {
bool value;
public:
BoolWrapper operator&&(const BoolWrapper& other) const {
return value && other.value;
}
};
六、运算符重载综合示例
智能指针实现
cpp
template<typename T>
class SmartPointer {
T* ptr;
public:
explicit SmartPointer(T* p = nullptr) : ptr(p) {}
~SmartPointer() { delete ptr; }
T& operator*() const { return *ptr; }
T* operator->() const { return ptr; }
explicit operator bool() const { return ptr != nullptr; }
};
七、不可重载的运算符
- 作用域解析运算符
::
- 成员访问运算符
.
- 条件运算符
?:
- 预处理符号
#
##
最佳实践原则:
- 保持运算符的直观语义
- 对称运算符(如算术运算符)应定义为非成员函数
- 复合赋值运算符(如
+=
)应返回左值引用 - 比较运算符应成对实现(C++20可用
<=>
简化) - 流运算符必须定义为友元非成员函数
- 下标运算符应提供常量和非常量版本
示例:三维向量类
cpp
class Vector3D {
double x, y, z;
public:
Vector3D operator+() const { return *this; } // 正号
Vector3D operator-() const { return {-x, -y, -z}; } // 负号
Vector3D& operator+=(const Vector3D& rhs) {
x += rhs.x;
y += rhs.y;
z += rhs.z;
return *this;
}
friend Vector3D operator+(Vector3D lhs, const Vector3D& rhs) {
lhs += rhs;
return lhs;
}
friend std::ostream& operator<<(std::ostream& os, const Vector3D& v) {
return os << "(" << v.x << ", " << v.y << ", " << v.z << ")";
}
};