目录
在C++中,操作符重载是一项强大的特性,它允许开发者为自定义类型(如类或结构体)重新定义内置操作符(如+
、-
、==
等)的行为。这种机制使得自定义类型能够像内置类型一样,通过操作符进行直观的操作。本文将深入探讨算数操作符和关系操作符的重载,结合代码示例和关键知识点,全面掌握这一重要特性。
一、操作符重载的核心价值
1.1 为什么要重载算术与关系操作符
在面向对象编程中,操作符重载赋予自定义类型与内置类型一致的操作体验。对于数学相关类型(如复数、矩阵、几何向量),合理的操作符重载可以:
-
提升代码可读性
-
增强类型安全性
-
实现数学表达式自然表达
// 未重载操作符
Matrix result = matrix1.add(matrix2.multiply(matrix3));
// 重载操作符后
Matrix result = matrix1 + matrix2 * matrix3;
1.2 操作符重载基本原则
原则 | 说明 | 示例验证 |
---|---|---|
语义一致性 | 操作符行为符合直觉 | a + b 应执行加法而非其他操作 |
完备性 | 相关操作符组应完整重载 | 重载== 必须同时重载!= |
效率优化 | 避免不必要的临时对象创建 | 使用复合赋值操作符优化运算 |
1.3 操作符重载的分类
分类 | 操作符示例 | 设计建议 |
---|---|---|
算术操作符 | + , - , * , / | 通常定义为非成员函数,不修改操作数状态,返回新对象。 |
关系操作符 | == , != , < , > , <= , >= | 通常定义为非成员函数,用于比较对象,支持STL容器(如std::sort )的排序需求。 |
赋值操作符 | = | 必须定义为成员函数,返回*this 的引用,支持链式赋值。 |
复合赋值操作符 | += , -= , *= , /= | 若类支持算术操作符,建议同时定义,提升代码可读性。 |
自增/自减操作符 | ++ , -- | 通常定义为成员函数,区分前缀(返回引用)和后缀(返回旧值)形式。 |
二、算数操作符重载
2.1 基本概念
算数操作符包括 +
、-
、*
、/
、%
等,用于执行基本的数学运算。在 C++ 中,我们可以为自定义类重载这些操作符,使得它们能够对类对象进行相应的运算。
2.2 重载加法操作符 +
下面以一个简单的 Complex
类(复数类)为例,展示如何重载加法操作符 +
。
#include <iostream>
class Complex {
private:
double real;
double imag;
public:
Complex(double r = 0, double i = 0) : real(r), imag(i) {}
// 重载加法操作符
Complex operator+(const Complex& other) const {
return Complex(real + other.real, imag + other.imag);
}
void display() const {
std::cout << real << " + " << imag << "i" << std::endl;
}
};
int main() {
Complex c1(1, 2);
Complex c2(3, 4);
Complex c3 = c1 + c2;
std::cout << "c1: ";
c1.display();
std::cout << "c2: ";
c2.display();
std::cout << "c1 + c2: ";
c3.display();
return 0;
}
定义了一个 Complex
类,并重载了加法操作符 +
。重载函数 operator+
接受一个 const Complex&
类型的参数,表示另一个复数对象,返回一个新的 Complex
对象,其实部和虚部分别是两个复数对象实部和虚部的和。
2.3 重载减法、乘法和除法操作符
类似地,我们可以重载减法、乘法和除法操作符。以下是完整的 Complex
类代码,包含了所有这些操作符的重载:
#include <iostream>
class Complex {
private:
double real;
double imag;
public:
Complex(double r = 0, double i = 0) : real(r), imag(i) {}
// 重载加法操作符
Complex operator+(const Complex& other) const {
return Complex(real + other.real, imag + other.imag);
}
// 重载减法操作符
Complex operator-(const Complex& other) const {
return Complex(real - other.real, imag - other.imag);
}
// 重载乘法操作符
Complex operator*(const Complex& other) const {
double newReal = real * other.real - imag * other.imag;
double newImag = real * other.imag + imag * other.real;
return Complex(newReal, newImag);
}
// 重载除法操作符
Complex operator/(const Complex& other) const {
double denominator = other.real * other.real + other.imag * other.imag;
double newReal = (real * other.real + imag * other.imag) / denominator;
double newImag = (imag * other.real - real * other.imag) / denominator;
return Complex(newReal, newImag);
}
void display() const {
std::cout << real << " + " << imag << "i" << std::endl;
}
};
int main() {
Complex c1(1, 2);
Complex c2(3, 4);
Complex sum = c1 + c2;
Complex diff = c1 - c2;
Complex prod = c1 * c2;
Complex quot = c1 / c2;
std::cout << "c1: ";
c1.display();
std::cout << "c2: ";
c2.display();
std::cout << "c1 + c2: ";
sum.display();
std::cout << "c1 - c2: ";
diff.display();
std::cout << "c1 * c2: ";
prod.display();
std::cout << "c1 / c2: ";
quot.display();
return 0;
}
2.4 重载复合赋值操作符
复合赋值操作符如 +=
、-=
、*=
、/=
等也可以被重载。以下是 Complex
类中重载 +=
操作符的示例:
#include <iostream>
class Complex {
private:
double real;
double imag;
public:
Complex(double r = 0, double i = 0) : real(r), imag(i) {}
// 重载 += 操作符
Complex& operator+=(const Complex& other) {
real += other.real;
imag += other.imag;
return *this;
}
void display() const {
std::cout << real << " + " << imag << "i" << std::endl;
}
};
int main() {
Complex c1(1, 2);
Complex c2(3, 4);
c1 += c2;
std::cout << "c1 after c1 += c2: ";
c1.display();
return 0;
}
operator+=
函数返回 *this
的引用,这样可以支持链式调用,例如 c1 += c2 += c3;
。
三、关系操作符重载
3.1 基本概念
关系操作符包括 ==
、!=
、<
、>
、<=
、>=
等,用于比较两个对象的大小或相等性。通过重载这些操作符,我们可以自定义类对象之间的比较规则。
3.2 重载相等操作符 ==
以下是一个 Point
类,用于表示二维平面上的点,并重载了相等操作符 ==
:
#include <iostream>
class Point {
private:
int x;
int y;
public:
Point(int _x = 0, int _y = 0) : x(_x), y(_y) {}
// 重载相等操作符
bool operator==(const Point& other) const {
return (x == other.x) && (y == other.y);
}
};
int main() {
Point p1(1, 2);
Point p2(1, 2);
Point p3(3, 4);
if (p1 == p2) {
std::cout << "p1 and p2 are equal." << std::endl;
} else {
std::cout << "p1 and p2 are not equal." << std::endl;
}
if (p1 == p3) {
std::cout << "p1 and p3 are equal." << std::endl;
} else {
std::cout << "p1 and p3 are not equal." << std::endl;
}
return 0;
}
operator==
函数接受一个 const Point&
类型的参数,表示另一个点对象,返回一个布尔值,表示两个点是否相等。
3.3 重载其他关系操作符
类似地,我们可以重载其他关系操作符。以下是 Point
类中重载 <
操作符的示例:
#include <iostream>
class Point {
private:
int x;
int y;
public:
Point(int _x = 0, int _y = 0) : x(_x), y(_y) {}
// 重载 < 操作符
bool operator<(const Point& other) const {
if (x < other.x) {
return true;
} else if (x == other.x) {
return y < other.y;
}
return false;
}
};
int main() {
Point p1(1, 2);
Point p2(3, 4);
Point p3(1, 3);
if (p1 < p2) {
std::cout << "p1 < p2" << std::endl;
} else {
std::cout << "p1 >= p2" << std::endl;
}
if (p1 < p3) {
std::cout << "p1 < p3" << std::endl;
} else {
std::cout << "p1 >= p3" << std::endl;
}
return 0;
}
operator<
函数定义了一种比较规则:先比较 x
坐标,如果 x
坐标相等,则比较 y
坐标。
四、操作符重载的注意事项
4.1 操作符重载的限制
- 只能重载已有的操作符,不能创建新的操作符。
- 不能改变操作符的优先级和结合性。
- 部分操作符(如
::
、.
、.*
、?:
等)不能被重载。
4.2 成员函数和非成员函数重载
操作符重载可以作为类的成员函数或非成员函数实现。一般来说,赋值操作符 =
、下标操作符 []
、函数调用操作符 ()
等通常作为成员函数重载,而二元操作符(如 +
、-
、*
、/
等)可以作为成员函数或非成员函数重载。
4.3 保持操作符的语义一致性
在重载操作符时,应尽量保持操作符的语义与内置类型的语义一致,避免造成混淆。例如,重载 +
操作符应该实现加法的语义,而不是减法。
五、总结
通过操作符重载,我们可以为自定义类对象赋予与内置类型相似的操作能力,从而提高代码的可读性和可维护性。在重载算数操作符和关系操作符时,需要注意操作符的语义一致性、重载的限制以及成员函数和非成员函数的选择。希望本文能帮助你更好地理解和掌握 C++ 中操作符重载的相关知识。
六、代码示例总结
6.1 复数类 Complex
完整代码
#include <iostream>
class Complex {
private:
double real;
double imag;
public:
Complex(double r = 0, double i = 0) : real(r), imag(i) {}
// 重载加法操作符
Complex operator+(const Complex& other) const {
return Complex(real + other.real, imag + other.imag);
}
// 重载减法操作符
Complex operator-(const Complex& other) const {
return Complex(real - other.real, imag - other.imag);
}
// 重载乘法操作符
Complex operator*(const Complex& other) const {
double newReal = real * other.real - imag * other.imag;
double newImag = real * other.imag + imag * other.real;
return Complex(newReal, newImag);
}
// 重载除法操作符
Complex operator/(const Complex& other) const {
double denominator = other.real * other.real + other.imag * other.imag;
double newReal = (real * other.real + imag * other.imag) / denominator;
double newImag = (imag * other.real - real * other.imag) / denominator;
return Complex(newReal, newImag);
}
// 重载 += 操作符
Complex& operator+=(const Complex& other) {
real += other.real;
imag += other.imag;
return *this;
}
void display() const {
std::cout << real << " + " << imag << "i" << std::endl;
}
};
int main() {
Complex c1(1, 2);
Complex c2(3, 4);
Complex sum = c1 + c2;
Complex diff = c1 - c2;
Complex prod = c1 * c2;
Complex quot = c1 / c2;
std::cout << "c1: ";
c1.display();
std::cout << "c2: ";
c2.display();
std::cout << "c1 + c2: ";
sum.display();
std::cout << "c1 - c2: ";
diff.display();
std::cout << "c1 * c2: ";
prod.display();
std::cout << "c1 / c2: ";
quot.display();
c1 += c2;
std::cout << "c1 after c1 += c2: ";
c1.display();
return 0;
}
6.2 点类 Point
完整代码
#include <iostream>
class Point {
private:
int x;
int y;
public:
Point(int _x = 0, int _y = 0) : x(_x), y(_y) {}
// 重载相等操作符
bool operator==(const Point& other) const {
return (x == other.x) && (y == other.y);
}
// 重载 < 操作符
bool operator<(const Point& other) const {
if (x < other.x) {
return true;
} else if (x == other.x) {
return y < other.y;
}
return false;
}
};
int main() {
Point p1(1, 2);
Point p2(1, 2);
Point p3(3, 4);
Point p4(1, 3);
if (p1 == p2) {
std::cout << "p1 and p2 are equal." << std::endl;
} else {
std::cout << "p1 and p2 are not equal." << std::endl;
}
if (p1 == p3) {
std::cout << "p1 and p3 are equal." << std::endl;
} else {
std::cout << "p1 and p3 are not equal." << std::endl;
}
if (p1 < p3) {
std::cout << "p1 < p3" << std::endl;
} else {
std::cout << "p1 >= p3" << std::endl;
}
if (p1 < p4) {
std::cout << "p1 < p4" << std::endl;
} else {
std::cout << "p1 >= p4" << std::endl;
}
return 0;
}