重载的概念:
重载(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
对象。 - 友元函数: 有时需要将运算符重载为类的友元函数,以便访问类的私有和保护成员。