定义
在C++中,运算符重载(Operator Overloading)是一种允许程序员为自定义数据类型重新定义或重载已有的运算符的功能。通过运算符重载,我们可以使得自定义类型的对象能够像内置类型(如int、float等)一样使用运算符。
运算符重载的规则
- 不能改变运算符的优先级和结合性:重载的运算符必须保持其原有的优先级和结合性。
- 不能创建新的运算符:我们只能重载C++中已经存在的运算符。
- 有些运算符不能被重载:如
.
、.*
、?:
、sizeof
、typeid
等运算符不能被重载。 - 重载运算符不能改变操作数的个数:重载的运算符必须保持原有的操作数个数。
- 重载运算符可以是成员函数或非成员函数:对于非成员函数,需要至少有一个参数是自定义类型;对于成员函数,第一个参数(即
this
指针)总是隐式的。
示例
下面是一个简单的示例,展示了如何为自定义的复数类重载加法运算符:
#include <iostream>
class Complex {
public:
double real;
double imag;
Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}
// 重载加法运算符作为成员函数
Complex operator+(const Complex& rhs) const {
return Complex(real + rhs.real, imag + rhs.imag);
}
void print() const {
std::cout << "(" << real << ", " << imag << ")" << std::endl;
}
};
int main() {
Complex c1(1, 2);
Complex c2(3, 4);
Complex sum = c1 + c2; // 使用重载的加法运算符
sum.print(); // 输出: (4, 6)
return 0;
}
编译运行
在这个例子中,我们定义了一个Complex
类来表示复数,并为其重载了加法运算符。重载的加法运算符是一个成员函数,它接受一个Complex
类型的右操作数(通过const Complex& rhs
参数),并返回一个新的Complex
对象,该对象的实部和虚部分别是两个操作数对应部分的和。
非成员函数运算符重载示例
有时,将运算符重载为非成员函数可能更为合适,特别是当运算符涉及两个不同类型的对象时。
示例
#include <iostream>
class Complex {
public:
double real;
double imag;
Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}
// 其他成员函数...
};
// 重载乘法运算符作为非成员函数
Complex operator*(const Complex& lhs, const Complex& rhs) {
return Complex(lhs.real * rhs.real - lhs.imag * rhs.imag,
lhs.real * rhs.imag + lhs.imag * rhs.real);
}
int main() {
Complex c1(1, 2);
Complex c2(3, 4);
Complex product = c1 * c2; // 使用重载的乘法运算符
// ...
return 0;
}
在这个例子中,乘法运算符被重载为一个非成员函数,它接受两个Complex
类型的参数,并返回一个新的Complex
对象,该对象表示两个复数的乘积。注意,非成员函数需要能够访问类的私有成员,因此通常需要将运算符重载函数声明为类的友元函数。然而,在这个例子中,由于我们只需要访问公共成员real
和imag
,因此不需要将运算符重载函数声明为友元。
什么运算符不能重载, 什么运算符能重载
在C++中,不是所有的运算符都可以被重载。以下是关于哪些运算符能重载、哪些不能重载以及为什么的详细解释,并附以例子。
不能重载的运算符
C++中不允许重载的运算符主要包括以下几类:
- 点运算符(
.
)和成员选择运算符(->*
):这些运算符用于访问对象的成员,重载它们会破坏语言的基本结构和语义。 - 作用域解析运算符(
::
):这个运算符用于指定命名空间或类的成员,重载它会破坏语言的命名空间机制。 - 三元条件运算符(
?:
):这个运算符的语义非常特定,且重载它可能会导致逻辑上的混乱。 sizeof
和typeid
运算符:这两个运算符用于获取类型信息,重载它们会破坏语言的类型系统。
能重载的运算符
除了上述不能重载的运算符外,C++中的其他运算符基本都可以被重载。这些运算符包括:
- 算术运算符:如
+
、-
、*
、/
、%
等,可以用于自定义类型的数值计算。 - 关系运算符:如
==
、!=
、<
、<=
、>
、>=
等,可以用于自定义类型的比较操作。 - 逻辑运算符:如
&&
、||
、!
等,可以用于自定义类型的逻辑判断。 - 位运算符:如
&
、|
、^
、~
、<<
、>>
等,可以用于自定义类型的位操作。 - 递增和递减运算符:
++
和--
,可以用于自定义类型的自增和自减操作。 - 赋值运算符:如
=
、+=
、-=
、*=
、/=
等,可以用于自定义类型的赋值操作。 - 下标运算符:
[]
,可以用于自定义类型的数组或容器访问。 - 调用运算符:
()
,可以用于自定义类型的函数调用。 - 成员访问运算符:
->
,通常用于指针类型的重载,以提供类似智能指针的语法糖。
为什么能重载这些运算符
这些运算符能够被重载的主要原因是,它们都有明确的语义,且重载它们不会破坏语言的基本结构和语义。通过重载这些运算符,我们可以使得自定义类型的对象能够像内置类型一样进行各种操作,从而提高代码的可读性和易用性。