重载的概念:
重载(Overloading)不仅仅局限于运算符,在C++中函数也可以重载,只要函数名称相同但参数列表不同,就可以视为重载函数。这样,根据传入的不同参数类型或数量,编译器就能正确识别调用的是哪一个版本的函数。
运算符重载(Operator Overloading)
运算符重载(Operator Overloading)是一种机制,为运算符赋予针对用户自定义类型的特定含义。这意味着我们可以为类或结构体重新定义(重载)运算符的行为,使其能够用于特定类型的对象。这有助于提高代码的清晰度和一致性,使得用户自定义类型的行为看起来更像是内置类型。
运算符重载——复数举例
class Complex {
public:
double r, i; // 实部和虚部
// 构造函数和其他成员函数略...
// 重载加法运算符 '+'
Complex operator+(const Complex& other) {
Complex result;
result.r = this->r + other.r;
result.i = this->i + other.i;
return result;
}
// 重载输出运算符 '<<',便于输出复数到ostream(如cout)
friend ostream& operator<<(ostream& os, const Complex& c) {
os << c.r << "+" << c.i << "i";
return os;
}
};
int main() {
Complex c1{1.0, 2.0};
Complex c2{3.0, 4.0};
// 使用重载的加法运算符
Complex sum = c1 + c2;
// 使用重载的输出运算符
cout << "Sum: " << sum << endl;
return 0;
}
为复数类重载了加法运算符+
,使得两个复数对象可以相加,得到一个新的复数对象。同时,我们也重载了输出运算符<<
,使得复数对象可以像内置类型一样方便地输出到标准输出流(如控制台)。这样,当调用c1 + c2
时,会执行我们定义的加法运算符函数,而不是C++内置的加法运算;同样,当我们使用cout << sum
时,会调用我们自定义的输出运算符函数来打印复数。
重载运算符的注意点
-
只能重载已存在的运算符: 你不能创建新的运算符,只能对C++语言中已经存在的运算符进行重载,如
+
,-
,*
,/
,==
,!=
,<
,>
,+=
,[]
,()
等。 -
至少有一个操作数是用户自定义类型: 当重载二元运算符时(例如
+
,-
),至少有一个操作数必须是类或结构类型的对象。也就是说,你不能为内置类型(如int
或double
)之间已有的运算符再提供新的含义。 -
运算符的语义: 重载后的运算符应保持原有的语义,除非这样做不符合类的设计目标。例如,如果重载
+
运算符用于两个复数对象相加,那么它应该遵循数学上的加法规则。 -
优先级和结合性不可改变: 重载运算符的优先级和结合性仍然是固定的,它们与对应的原始C++运算符保持一致。
-
成员函数与非成员函数: 二元运算符可以作为成员函数(有一个隐含的
this
参数)或非成员函数(接受两个显式的参数)。例如,作为成员函数的operator+
只需一个参数,而非成员函数则需要两个。 -
特殊运算符: 有些运算符不能被重载,如
.
,.*
,::
,?:
,sizeof
等。 -
操作数的数量和顺序: 重载运算符时,不能更改操作数的数量和顺序。例如,如果原先是二元运算符,重载后也必须是二元的。
-
返回类型: 根据运算符的具体语义,返回类型应合理选择,比如
+
运算符可能返回一个新创建的对象,而赋值运算符=
通常返回左侧对象自身的引用以便支持链式赋值。 -
异常安全性: 在设计重载运算符时,特别是在处理资源管理类(如智能指针)时,应考虑异常安全性,确保即使在异常抛出的情况下也能正确释放资源。
-
短路逻辑: 对于逻辑运算符
&&
和||
,重载后将不再具有短路特性,即不论左侧表达式的值如何,右侧都会被执行。 -
const成员函数: 如果重载的运算符不会修改类的状态,应声明为
const
成员函数,这样它才能应用于const
对象。 -
友元函数: 有时需要将运算符重载为类的友元函数,以便访问类的私有和保护成员。