C++运算符重载全面总结
运算符重载是C++中一项强大的特性,它允许程序员为自定义类型定义运算符的行为。以下是关于C++运算符重载的详细总结:
一、基本概念
1. 什么是运算符重载
运算符重载是指为自定义类型(类或结构体)重新定义或重载已有的运算符,使其能够操作该类型的对象。
2. 运算符重载的限制
- 不能创建新的运算符
- 不能改变运算符的优先级和结合性
- 不能改变运算符的操作数个数
- 部分运算符不能被重载(如
::
,.*
,.
,?:
等) - 重载运算符至少有一个操作数是用户定义类型
二、运算符重载的实现方式
1. 成员函数重载
运算符作为类的成员函数重载,隐含this
指针作为第一个操作数。
class Complex {
public:
Complex operator+(const Complex& other) const {
return Complex(real + other.real, imag + other.imag);
}
private:
double real, imag;
};
2. 友元函数重载
运算符作为非成员函数重载,通常需要声明为友元以访问私有成员。
class Complex {
friend Complex operator+(const Complex& a, const Complex& b);
};
Complex operator+(const Complex& a, const Complex& b) {
return Complex(a.real + b.real, a.imag + b.imag);
}
3. 全局函数重载
对于不访问私有成员的运算符,可以直接定义为全局函数。
Point operator+(const Point& a, const Point& b) {
return Point(a.x() + b.x(), a.y() + b.y());
}
三、可重载运算符分类
1. 算术运算符
+
, -
, *
, /
, %
, +=
, -=
, *=
, /=
, %=
class Complex {
public:
Complex& operator+=(const Complex& other) {
real += other.real;
imag += other.imag;
return *this;
}
};
2. 关系运算符
==
, !=
, <
, >
, <=
, >=
bool operator==(const Point& a, const Point& b) {
return a.x() == b.x() && a.y() == b.y();
}
3. 逻辑运算符
!
, &&
, ||
class Boolean {
public:
bool operator!() const { return !value; }
};
4. 位运算符
&
, |
, ^
, ~
, <<
, >>
, &=
, |=
, ^=
, <<=
, >>=
class BitMask {
public:
BitMask operator<<(int shift) const {
return BitMask(mask << shift);
}
};
5. 赋值运算符
=
, +=
, -=
, *=
, /=
, %=
, &=
, |=
, ^=
, <<=
, >>=
class String {
public:
String& operator=(const String& other) {
// 实现深拷贝
return *this;
}
};
6. 递增递减运算符
++
, --
(前缀和后缀)
class Counter {
public:
// 前缀++
Counter& operator++() {
++count;
return *this;
}
// 后缀++
Counter operator++(int) {
Counter temp = *this;
++count;
return temp;
}
};
7. 下标运算符
[]
class Array {
public:
int& operator[](size_t index) {
return data[index];
}
const int& operator[](size_t index) const {
return data[index];
}
};
8. 函数调用运算符
()
class Functor {
public:
int operator()(int x, int y) {
return x + y;
}
};
9. 成员访问运算符
->
, ->*
class SmartPtr {
public:
T* operator->() const {
return ptr;
}
};
10. 类型转换运算符
operator type()
class Rational {
public:
operator double() const {
return static_cast<double>(numerator) / denominator;
}
};
11. 内存管理运算符
new
, new[]
, delete
, delete[]
class MemoryPool {
public:
void* operator new(size_t size) {
return pool.allocate(size);
}
void operator delete(void* p) {
pool.deallocate(p);
}
};
四、特殊运算符重载注意事项
1. 输入输出运算符
<<
, >>
通常应作为友元函数重载
class Complex {
friend ostream& operator<<(ostream& os, const Complex& c);
friend istream& operator>>(istream& is, Complex& c);
};
ostream& operator<<(ostream& os, const Complex& c) {
return os << c.real << "+" << c.imag << "i";
}
2. 赋值运算符
- 应返回
*this
的引用以支持链式赋值 - 需要处理自赋值情况
- 通常需要实现拷贝赋值和移动赋值
class String {
public:
String& operator=(const String& other) {
if (this != &other) {
// 实现深拷贝
}
return *this;
}
String& operator=(String&& other) noexcept {
// 实现移动语义
return *this;
}
};
3. 类型转换运算符
- 可以声明为
explicit
防止隐式转换 - 应谨慎使用以避免意外的类型转换
class SafeBool {
public:
explicit operator bool() const { return valid; }
};
五、运算符重载的最佳实践
- 保持直观性:重载的运算符行为应与内置类型相似
- 一致性:相关运算符应一起重载(如
==
和!=
,<
和>
等) - 避免过度重载:只为有意义的操作重载运算符
- 考虑对称性:对于二元运算符,考虑是否需要支持交换操作数的顺序
- 性能考虑:尽可能使用引用和移动语义提高效率
- 异常安全:确保运算符重载是异常安全的
六、运算符重载示例
完整示例:复数类
class Complex {
public:
Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}
// 成员函数重载
Complex operator+(const Complex& other) const {
return Complex(real + other.real, imag + other.imag);
}
Complex& operator+=(const Complex& other) {
real += other.real;
imag += other.imag;
return *this;
}
// 前缀++
Complex& operator++() {
++real;
return *this;
}
// 后缀++
Complex operator++(int) {
Complex temp = *this;
++real;
return temp;
}
// 友元函数重载
friend Complex operator-(const Complex& a, const Complex& b);
friend std::ostream& operator<<(std::ostream& os, const Complex& c);
// 比较运算符
bool operator==(const Complex& other) const {
return real == other.real && imag == other.imag;
}
bool operator!=(const Complex& other) const {
return !(*this == other);
}
private:
double real, imag;
};
// 非成员函数实现
Complex operator-(const Complex& a, const Complex& b) {
return Complex(a.real - b.real, a.imag - b.imag);
}
std::ostream& operator<<(std::ostream& os, const Complex& c) {
os << c.real << (c.imag >= 0 ? "+" : "") << c.imag << "i";
return os;
}
七、C++20新增特性
1. 三路比较运算符 <=>
C++20引入了三路比较运算符,可以简化比较运算符的实现:
class Point {
public:
auto operator<=>(const Point&) const = default;
// 自动生成 ==, !=, <, <=, >, >=
};
2. 重载co_await
C++20协程支持重载co_await
运算符。
总结
运算符重载是C++中实现自定义类型行为一致性的重要手段。合理使用运算符重载可以使代码更直观、更易读。但同时也要注意不要滥用,确保重载的运算符行为符合直觉,并遵循语言惯例。