运算符重载是 C++ 面向对象编程的重要特性,它让自定义类对象 可以像普通基本数据类型(int、double 等)一样使用+、-、*、/、==、++、--等运算符,大幅提升代码的可读性和简洁性。本文从核心概念、语法、常用运算符重载实现、注意事项全面讲解,帮你快速掌握。
一、运算符重载基础
1. 什么是运算符重载?
本质:给 C++ 原有运算符重新定义功能,使其能处理自定义类的对象。
运算符本质是一种特殊的函数,重载就是定义这个函数;
不创建新运算符,仅对现有运算符重新实现;
不改变运算符的优先级、结合性和操作数个数。
2. 语法格式
作为类成员函数重载(最常用):
cpp
返回值类型 operator 运算符(参数列表) {
// 实现逻辑
}
作为全局函数重载:
cpp
返回值类型 operator 运算符(参数列表) {
// 实现逻辑
}
关键字:operator + 要重载的运算符(如operator+、operator==)
3. 调用方式
两种写法完全等价:
cpp
// 隐式调用(简洁,推荐)
对象1 + 对象2;
// 显式调用(本质,编译器底层执行)
对象1.operator+(对象2);
二、常用运算符重载实战
我们以 ** 坐标类(Point)** 为例,实现最常用的运算符重载,包含:+、-、==、!=、前置++、后置++、赋值运算符=
1. 加号运算符 +
实现两个坐标对象相加,返回新的坐标对象。
cpp
#include <iostream>
using namespace std;
class Point {
private:
int x, y;
public:
Point(int x = 0, int y = 0) : x(x), y(y) {}
// 成员函数重载 + :1个参数(右侧运算对象)
Point operator+(const Point& p) {
return Point(x + p.x, y + p.y);
}
void show() {
cout << "x=" << x << ", y=" << y << endl;
}
};
int main() {
Point p1(1, 2), p2(3, 4);
Point p3 = p1 + p2; // 等价 p1.operator+(p2)
p3.show(); // 输出:x=4, y=6
return 0;
}
2. 相等 / 不等运算符 == / !=
判断两个对象的成员变量是否相等:
cpp
// 在Point类中添加
bool operator==(const Point& p) {
return x == p.x && y == p.y;
}
bool operator!=(const Point& p) {
return !(*this == p); // 复用==代码
}
3. 自增运算符 ++(重点区分前置 / 后置)
这是面试高频考点,前置 ++ 和后置 ++ 重载语法不同:
cpp
// 1. 前置++:返回引用,效率更高
Point& operator++() {
x++;
y++;
return *this;
}
// 2. 后置++:int是占位参数,区分前置,返回临时对象
Point operator++(int) {
Point temp = *this; // 保存旧值
x++;
y++;
return temp; // 返回旧值
}
4. 赋值运算符 =(必须掌握)
编译器会默认生成赋值运算符,但类中有指针成员时必须手动重载(深拷贝),否则会出现和浅拷贝一样的内存崩溃问题。
cpp
// 赋值运算符重载(深拷贝示例)
class Student {
private:
char* name;
public:
Student(const char* n) {
name = new char[strlen(n)+1];
strcpy(name, n);
}
// 重载= 解决浅拷贝问题
Student& operator=(const Student& s) {
// 1. 先释放自身原有内存
if (name != NULL) {
delete[] name;
name = NULL;
}
// 2. 深拷贝:重新分配内存+复制数据
name = new char[strlen(s.name)+1];
strcpy(name, s.name);
return *this;
}
~Student() {
delete[] name;
}
};
5. 左移运算符 <<(全局函数重载)
用于直接输出对象,必须重载为全局函数(友元):
cpp
// 类内声明友元
friend ostream& operator<<(ostream& out, const Point& p);
// 类外实现
ostream& operator<<(ostream& out, const Point& p) {
out << "x=" << p.x << ", y=" << p.y;
return out; // 支持链式调用:cout << p1 << p2
}
// 使用
cout << p1 << endl;
三、成员函数 VS 全局函数重载
表格
| 运算符类型 | 重载方式 | 参数个数 | 说明 |
|---|---|---|---|
| 双目运算符(+ - * / ==) | 成员函数 | 1 个 | 左侧对象是 this 指针 |
| 单目运算符(++ --) | 成员函数 | 0 个 / 1 个(占位) | 前置无参,后置 int 占位 |
| 左移 / 右移(<<>>) | 全局函数(友元) | 2 个 | 不能用成员函数 |
| 赋值运算符(=) | 成员函数 | 1 个 | 只能成员重载 |
四、不能重载的运算符
C++ 中有 5 个运算符无法重载,牢记即可:
- 成员访问运算符:
. - 作用域解析符:
:: - 三目运算符:
?: - 长度运算符:
sizeof - 类型 id 运算符:
typeid
五、运算符重载三大原则
- 不改变原有语义:+ 就做加法,不要实现减法逻辑;
- 能成员重载就不全局:成员函数可以直接访问私有成员,更简洁;
- 有指针必重载 =:赋值运算符必须做深拷贝,防止内存崩溃。
六、完整示例代码
cpp
#include <iostream>
using namespace std;
class Point {
private:
int x, y;
public:
Point(int x=0, int y=0) : x(x), y(y) {}
// +
Point operator+(const Point& p) {
return Point(x+p.x, y+p.y);
}
// ==
bool operator==(const Point& p) {
return x==p.x && y==p.y;
}
// 前置++
Point& operator++() {
x++, y++;
return *this;
}
// 后置++
Point operator++(int) {
Point temp = *this;
x++, y++;
return temp;
}
// 友元声明
friend ostream& operator<<(ostream& out, const Point& p);
};
// 重载<<
ostream& operator<<(ostream& out, const Point& p) {
out << "x=" << p.x << ", y=" << p.y;
return out;
}
int main() {
Point p1(1,2), p2(3,4);
Point p3 = p1 + p2;
cout << "p1+p2: " << p3 << endl;
++p3;
cout << "前置++: " << p3 << endl;
p3++;
cout << "后置++: " << p3 << endl;
cout << "p1==p2? " << (p1==p2) << endl;
return 0;
}
总结
- 运算符重载是
operator+运算符的函数,让对象支持常规运算; - 成员函数重载最常用,
<< / >>必须全局友元重载; - 前置 ++ 返回引用效率高,后置 ++ 用 int 占位区分;
- 类有指针成员时,必须重载赋值运算符做深拷贝;
- 不重载
.、::、?:、sizeof、typeid。