本文将深入浅出地讲解C++运算符重载的核心概念和实用技巧,通过丰富示例带你掌握这一强大特性
一、什么是运算符重载?
运算符重载是C++中一项强大的特性,它允许我们为自定义类型(类或结构体)定义运算符的行为。就像给自定义类型"教"会运算符的使用方法,让它们可以像内置类型(int, float等)一样使用运算符进行运算。
// 未重载运算符时的使用方式
Vector3 v1(1, 2, 3);
Vector3 v2(4, 5, 6);
Vector3 result = v1.add(v2); // 普通函数调用
// 重载运算符后的使用方式
Vector3 result = v1 + v2; // 使用直观的+运算符
二、运算符重载的基本规则
可重载运算符列表
运算符类型 |
具体运算符 |
算术运算符 |
|
关系运算符 |
|
逻辑运算符 |
|
位运算符 |
|
赋值运算符 |
|
| 其他 | [ ]
()
->
,
new
delete
new[ ]
delete[ ]
|
不可重载运算符
-
成员访问运算符
.
-
成员指针访问运算符
.*
-
域解析运算符
::
-
条件运算符
?:
-
sizeof
和typeid
重载限制
-
不能创建新运算符
-
不能改变运算符的优先级和结合性
-
不能改变运算符的操作数个数
-
至少有一个操作数是用户自定义类型
三、两种重载方式:成员函数 vs 友元函数
1. 成员函数形式
class Vector3 {
public:
// 成员函数形式重载+
Vector3 operator+(const Vector3& other) const {
return Vector3(x + other.x, y + other.y, z + other.z);
}
private:
double x, y, z;
};
特点:
-
运算符作为类的成员函数
-
左操作数默认为当前对象(
this
) -
适合一元运算符和赋值类运算符
2. 友元函数形式
class Vector3 {
public:
// 声明友元函数
friend Vector3 operator*(double scalar, const Vector3& vec);
private:
double x, y, z;
};
// 友元函数实现
Vector3 operator*(double scalar, const Vector3& vec) {
return Vector3(scalar * vec.x, scalar * vec.y, scalar * vec.z);
}
特点:
-
运算符作为普通函数(通常声明为友元)
-
可以自由定义左右操作数的类型
-
适合需要对称性的运算符(如
<<
,>>
, 算术运算符等)
四、常用运算符重载实战
1. 输入输出运算符重载(>> 和 <<)
class Student {
public:
friend std::ostream& operator<<(std::ostream& os, const Student& s);
friend std::istream& operator>>(std::istream& is, Student& s);
private:
std::string name;
int age;
};
std::ostream& operator<<(std::ostream& os, const Student& s) {
os << "Name: " << s.name << ", Age: " << s.age;
return os;
}
std::istream& operator>>(std::istream& is, Student& s) {
std::cout << "Enter name: ";
is >> s.name;
std::cout << "Enter age: ";
is >> s.age;
return is;
}
// 使用示例
Student stu;
std::cin >> stu; // 输入学生信息
std::cout << stu; // 输出学生信息
2. 算术运算符重载(+, -, *, /)
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);
}
// 友元函数形式重载*
friend Complex operator*(const Complex& c1, const Complex& c2);
private:
double real, imag;
};
Complex operator*(const Complex& c1, const Complex& c2) {
return Complex(
c1.real * c2.real - c1.imag * c2.imag,
c1.real * c2.imag + c1.imag * c2.real
);
}
// 使用示例
Complex c1(1, 2), c2(3, 4);
Complex sum = c1 + c2; // (4, 6)
Complex product = c1 * c2; // (1*3 - 2*4, 1*4 + 2*3) = (-5, 10)
3. 自增自减运算符(++ 和 --)
class Counter {
public:
Counter() : count(0) {}
// 前置++
Counter& operator++() {
++count;
return *this;
}
// 后置++ (int参数用于区分)
Counter operator++(int) {
Counter temp = *this;
++count;
return temp;
}
int getCount() const { return count; }
private:
int count;
};
// 使用示例
Counter c;
Counter c1 = ++c; // 前置:c=1, c1=1
Counter c2 = c++; // 后置:c=2, c2=1
4. 下标运算符([ ])
class IntArray {
public:
IntArray(int size) : size(size) {
data = new int[size];
}
~IntArray() {
delete[ ] data;
}
// 下标运算符重载
int& operator[ ](int index) {
if (index < 0 || index >= size) {
throw std::out_of_range("Index out of range");
}
return data[index];
}
// const版本
const int& operator[ ](int index) const {
if (index < 0 || index >= size) {
throw std::out_of_range("Index out of range");
}
return data[index];
}
private:
int* data;
int size;
};
// 使用示例
IntArray arr(5);
arr[0] = 10; // 写操作
int value = arr[0]; // 读操作
5. 函数调用运算符(())
class Adder {
public:
Adder(int base) : base(base) {}
int operator()(int value) const {
return base + value;
}
private:
int base;
};
// 使用示例
Adder add5(5);
std::cout << add5(10); // 输出15
// 在STL算法中使用
std::vector<int> nums {1, 2, 3, 4};
std::transform(nums.begin(), nums.end(), nums.begin(), Adder(3));
// nums变为{4, 5, 6, 7}
五、特殊运算符重载
1. 赋值运算符(=)
class String {
public:
// 赋值运算符
String& operator=(const String& other) {
if (this != &other) { // 防止自赋值
delete[ ] data; // 释放原有资源
size = other.size;
data = new char[size + 1];
strcpy(data, other.data);
}
return *this;
}
// 移动赋值运算符 (C++11)
String& operator=(String&& other) noexcept {
if (this != &other) {
delete[ ] data;
data = other.data; // 资源转移
size = other.size;
other.data = nullptr; // 置空源对象
other.size = 0;
}
return *this;
}
private:
char* data;
size_t size;
};
2. 类型转换运算符
class Rational {
public:
Rational(int n = 0, int d = 1) : num(n), den(d) {}
// 转换为double类型
operator double() const {
return static_cast<double>(num) / den;
}
// 转换为字符串
operator std::string() const {
return std::to_string(num) + "/" + std::to_string(den);
}
private:
int num, den;
};
// 使用示例
Rational r(3, 4);
double d = r; // 0.75
std::string s = r; // "3/4"
六、最佳实践与注意事项
保持运算符的直观语义
-
+
应该做加法,而不是其他操作 -
==
应该实现相等比较
成对重载相关运算符
-
重载
==
时也应重载!=
-
重载
<
时考虑重载>
、<=
、>=
正确处理自赋值
-
在赋值运算符中检查
if(this != &rhs)
返回引用以提高效率
-
赋值运算符应返回
T&
以支持链式赋值a = b = c
区分前置和后置运算符
-
前置版本返回引用:
T& operator++()
-
后置版本返回值并带int参数:
T operator++(int)
考虑异常安全性
-
确保操作不会因异常而破坏对象状态
七、总结
运算符重载是C++强大的特性之一,合理使用可以:
-
提高代码可读性
-
增强类型一致性
-
简化复杂操作
-
使自定义类型与内置类型一样自然
掌握运算符重载能让你的C++代码更加优雅高效。记住核心原则:保持语义直观,避免滥用。当运算符重载符合用户预期时,它能成为提升代码质量的强大工具!