运算符重载(Operator Overloading)
操作符重载的要点
- 操作符的通用语法
- 双目操作符:<左操作数><操作符><右操作数>,简单表示为,L#R。
- 单目操作符:<操作数><操作符>或<操作符><操作数>,简单表示为,O#或#O。
- 被重载操作符的操作数中至少有一个是类类型或枚举类型。
- 被重载操作符的操作数中至少有一个不是内建类型,如int,char,double等及其指针或引用。
- 操作符的优先级不会因其被重载而发生改变。
- 操作符的操作数个数不会因其被重载而发生改变。
- 除“()”之外的所有操作符都不能接受缺省参数。
- 并不是所有操作符都能被重载,如::、.*、.和?:,等等。
操作符标记与操作符函数
- 将一个操作符标记#应用于一个或多个类类型的操作数时,编译器将调用与这个操作符标记相关联的操作符函数operator#()。
-----------+------------
操作符标记 | 操作符函数
-----------+------------
= | operator=
+ | operator+
+= | operator+=
++ | operator++
<< | operator<<
[] | operator[]
... | ...
-----------+------------
- 操作符函数的重载定义通常包括全局函数和成员函数两种形式。
输入输出操作符
- 一般而言,如果一个输入输出操作符#已经按照全局函数的方式被重载定义了,那么表达式
L#R
将被编译器解释为如下函数调用:
operator#(L,R)
可见,按照全局函数方式被重载定义的输入输出操作符函数,应该有两个参数,分别是该操作符的左操作数和右操作数。
范例:io_operator.cpp - 为了能够在操作符的全局函数重载定义中直接访问类的私有成员,我们不妨将其声明为该类的友元。
范例:io_friend.cpp - 友元声明可以出现在一个类的私有、保护或公共部分,它允许被声明者直接访问该类的所有数据成员和成员函数,无论其访问控制属性是私有的、保护的还是公共的。
范例:friend.cpp
注意:重载输入输出操作符,重载的对象是std::cin
和std::cout
。
双目操作符
一般而言,如果一个双目操作符#已经按照成员函数的方式被重载定义了,那么表达式,⬇️
L#R
将被编译器解释为如下函数调用:
L.operator#(R)
可见,按照成员函数方式被重载定义的双目操作符函数,只有一个参数,即该操作符的右操作数。而该操作符的左操作数将成为此成员函数形式操作符函数的调用对象。
范例:membinary.cpp按照成员函数方式被重载定义的双目操作符函数,最终将被其左操作数所调用,且传入其右操作数实参。因此,该操作符函数显然应该被定义为左操作数类型的成员函数,且带有一个右操作数类型的形
输出(或输入)操作符左操作数的类型为ostream(或istream),因此我们即使能够按照成员函数的方式为其提供重载定义,也应该将其定义为ostream(或istream)的成员函数,而我们无法为这个输出(或输入)流类自行添加新的成员函数。因此,我们别无选择,只能对输出(或输入)操作符进行全局重载定义。但是,我们仍然可以将输出(或输入)操作符的全局重载定义声明为被输出(或输入)对象类型的友元,以使它更接近于成员函数的本质。
单目操作符
一般而言,如果一个单目操作符#已经按照成员函数的方式被重载定义了,那么表达式:
#O
将被编译器解释为如下函数调用:
O.operator#()
可见,按照成员函数方式被重载定义的单目操作符函数,没有任何参数。而该操作符唯一的一个操作数将成为此成员函数形式的操作符函数的调用对象。
范例:memunary.cpp
自增自减操作符
++O ==> O.operator++()
O++ ==> O.operator++(0)
--O ==> O.operator--()
O-- ==> O.operator--(0)
注意:前缀操作符可以重复使用。
范例:memself.cpp
成员还是友元
- 一个操作符的左右操作数不一定都是相同类型的对象,这就涉及到将该操作符函数定义为谁的成员,谁的友元的问题。
例如:
cin>>a
/*操作对象*//*操作符*//*被操作对象*/
/*需要重载的对象*//*需要重载的操作符*//*被操作对象*/
一个操作符函数被声明为哪个类的友元,取决于该函数参数对象的类型。
一个操作符函数被定义为哪个类的成员,取决于该函数调用对象的类型。
范例:friend2.cpp
类型转换操作符与自定义类型转换
通过类型转换操作符可实现自定义类型转换。类型转换操作符只能被定义为源类型的成员函数,而不能被定义为全局函数。当需要进行类型转换时(构造、赋值、函数调用等),编译器将产生如下函数调用:
obj.operator int ();
范例:OperatorTypeConversion.cpp通过构造函数实现自定义类型转换。以源类型对象为参数,通过目标类型中特定的构造函数,创建一个目标类型的对象。
范例:OperatorTypeConversion02.cpp在源类型中定义类型转换运算符,同时为目标类型提供以源类型对象为参数的构造函数,不同的类型转换方式会有各自的匹配策略:通过类型转换运算符完成隐式类型转换,通过构造函数完成静态类型转换。
范例:OperatorTypeConversion02.cpp为具有类型转换性质的构造函数添加explicit修饰符,表明此构造函数仅支持显式类型转换。通过这种机制可有效地防止误转换。
范例:OperatorTypeConversion03.cpp为类型转换运算符函数添加explicit修饰符,表明此函数仅支持显式类型转换。通过这种机制可有效地防止误转换。
范例:OperatorTypeConversion04.cpp
说明:此特性要求编译器支持C++0X标准,g++ -std=c++11 OperatorTypeConversion04.cpp
。
解引用操作符(*和->)与智能指针
函数操作符
new/delete操作符
newdel.cpp
说明:此特性要求编译器支持C++11标准,g++ -std=c++11 newdel.cpp
。