第 13章 运算符重载
13.1 运算符重载概述
13.2 运算符重载的实现
13.3 单目运算符重载
13.4 双目运算符重载
13.5 ->运算符重载
13.6 赋值运算符重载
13.7 下标运算符重载
13.8 运算符 new与 delete重载
13.13 逗号运算符重载
13.10 类型转换运算符重载
13.11 运算符重载应用实例
C++语言允许程序员重新定义已有的运算
符,使其能按用户的要求完成一些特定的操作,
这就是所谓的运算符重载。运算符重载与函数
重载相似,其目的是设置某一运算符,让它具
有另一种功能,尽管此运算符在原先 C++语言
中代表另一种含义,但他们彼此之间并不冲突。
C++会根据运算符的位置辨别应使用哪一种功
能进行运算。
13.1 运算符重载概述
运算符重载是对已有的运算符赋予多重含义, 同
一个运算符作用于不同类型的数据导致不同类型的行
为 。 C++中预定义的运算符的操作对象只能是基本数
据类型, 实际上, 对于很多用户自定义类型, 也需要
有类似的运算操作, 这就提出了对运算符进行重新定
义, 赋予已有符号以新功能的要求 。
运算符重载的实质就是函数重载。在实现过程中,
首先把指定的运算表达式转化为对运算符函数的调用,
运算对象转化为运算符函数的实参,然后根据实参的
类型来确定需要调用的函数,这个过程是在编译过程
中完成的。
C++可以重载的运算符如表 13-1所示 ;
C++不能被重载的运算符如表 13-2所示。
运算符重载的规则如下:
( 1) C++中的运算符除了少数几个以外, 全部可
以重载, 而且只能重载已有的这些运算符 。
( 2) 重载之后运算符的优先级和结合性都不会改
变 。
( 3)运算符重载是针对新类型数据的实际需要,
对原有运算符进行适当的改造。一般来讲,重载的功
能应当与原有功能相类似,不能改变原运算符的操作
对象个数,同时至少要有一个操作对象是自定义类型。
13.2 运算符重载的实现
运算符的重载形式有两种:重载为类的成员函数
和重载为类的友元函数 。
运算符重载为类的成员函数的语法形式如下:
<函数类型 > operator <运算符 >( <形参表 >)
{
<函数体 >;
}
friend <函数类型 > operator <运算符 >( <形参表 >)
{
<函数体 >;
}
其中, <函数类型 >指定了重载运算符的返回值类型,
operator是定义运算符重载函数的关键词, <运算符 >给
定了要重载的运算符名称, 是 C++中可重载的运算符,
形参表中给出重载运算符所需要的参数和类型 。 对于
运算符重载为友元函数的情况, 还要在函数类型说明
之前使用 friend关键词来说明 。
当运算符重载为类的成员函数时,函数的参数个
数比原来的运算数个数要少一个(后缀 ++,--除外);
当重载为类的友元函数时,参数个数与原运算数的个
数相同。
一般来讲,单目运算符最好重载为成员函数,而
双目运算符则最好重载为友元函数。运算符重载的主
要优点就是允许改变使用于系统内部的运算符的操作
方式,以适应用户新定义类型的类似运算。
13.3 单目运算符重载
类的单目运算符可重载为一个没有参数的非静态
成员函数或者带有一个参数的非成员函数, 参数必须
是用户字定义类型的对象或者是对该对象的引用 。
在 C++中, 单目运算符有 ++和 --,它们是变量自
动增 1和自动减 1的运算符 。 在类中可以对这两个单目
运算符进行重载 。
如同, ++”运算符有前缀, 后缀两种使用形式,
,++”和, --”重载运算符也有前缀和后缀两种运算符
重载形式, 以, ++”重载运算符为例, 其语法格式如
下:
<函数类型 > operator ++( ) ; //前缀运算
<函数类型 > operator ++( int); //后缀运算
使用前缀运算符的语法格式如下:
++<对象 >;
使用后缀运算符的语法格式如下:
<对象 >++;
使用运算符前缀时, 对对象 ( 操作数 ) 进行增量
修改, 然后再返回该对象 。 所以前缀运算符操作时,
参数与返回的是同一个对象 。 这与基本数据类型的运
算符前缀类似, 返回的也是左值 。
使用运算符后缀时,必须再增量之前返回原有的
对象值。为此,需要创建一个临时对象,存放原有的
对象,以便对操作数(对象)进行增量修改时,保存
最初的值。运算符后缀操作时返回的时原有对象值,
不是原有对象,原有对象已经被增量修改,所以,返
回的应该是存放原有对象值的临时对象。
13.4 双目运算符重载
对于双目运算符, 一个运算数是对象本身的数据,
由 this指针给出, 另一个运算数则需要通过运算符重载
函数的参数表来传递 。 下面分别介绍这两种情况 。
对于双目运算符 B,如果要重载 B为类的成员函数,
使之能够实现表达式,oprd1 B oprd2”,其中 oprd1为
A类的对象,则应当把 B重载为 A类的成员函数,该函
数只有一个形参,形参的类型是 oprd2所属的类型。经
过重载之后,表达式 oprd1 B oprd2就相当于函数调用
,oprd1.operator B( oprd2)”。
运算符也可以重载为类的友元函数,这样,它就
可以自由地访问该类的任何数据成员。这时,运算符
所需要的运算数都需要通过函数的形参表来传递,在
参数表中形参从左到右的顺序就是运算符运算数的顺
序。
13.5 ->运算符重载
“->”运算符是成员访问运算符, 这种一元的运算
符只能被重载为成员函数, 所以也决定了它不能定义
任何参数 。 一般成员访问运算符的典型用法是:
对象 ->成员
成员访问运算符, ->”函数重载的一般形式为:
type class_name::operator->( ) ;
13.6 赋值运算符重载
在 C++中有两种类型的赋值运算符:一类是, +=”
和, -=”等先计算后赋值的运算符, 另一类是, =”即直
接赋值的运算符 。 下面分别进行讨论 。
13.6.1 运算符, +=”和, -=”的重载
对于标准数据类型,,+=”和,-=”的作用是将一
个数据与另一个数据进行加法或减法运算后再将结果
回送给赋值号左边的变量中。对它们重载后,使其实
现其他相关的功能。
例 13-8, 程序中重载运算符,+=”和,-=”与标准
数据类型的,+=”和,-=”不完全相同。调用重载的运
算符时,例如 v1+=v2,并不改变 v1的值,而后者会改
变运算符左边变量的值。
13.6.2 运算符, =”的重载
赋值运算符, =”的原有含义是将赋值号右边表达
式的结果拷贝给赋值号左边的变量, 通过运算符, =”
的重载将赋值号右边对象的私有数据依次拷贝到赋值
号左边对象的私有数据中 。 在正常情况下, 系统会为
每一个类自动生成一个默认的完成上述功能的赋值运
算符, 当然, 这种赋值只限于由一个类类型说明的对
象之间赋值 。
如果一个类包含指针成员,采用这种默认的按成
员赋值,那么当这些成员撤消后,内存的使用将变得
不可靠。
可以重载运算符, =”来解决这个问题 。 重载该运
算符的成员函数如下:
Sample &operator = (Sample &s)
{
delete p;
p=new char[strlen(s.p)+1];
strcpy(p,s.p);
return *this;
}
把 s.p的内存复制到
this.p的内存中,而
不是仅仅进行指针复制
13.7 下标运算符重载
下标运算符, [ ]”通常用于在数组中标识数组元素
的位置, 下标运算符重载可以实现数组数据的赋值和
取值 。 下标运算符重载函数只能作为类的成员函数,
不能作为类的友元函数 。
下标运算符, [ ]”函数重载的一般形式为:
type class_name::operator[ ]( arg_) ;
其中 arg为该重载函数的参数。重载了的下标运算
符只能且必须带一个参数,该参数给出下标的值。重
载函数 operator[ ]的返回值类型 type是引用类型。
13.8 运算符 new与 delete重载
C++提供了 new与 delete两个运算符用于内存管理,
但有些情况下用户需要自己管理内存, 为自己所定义
的类体系建立一种新的动态内存管理算法, 以克服 new
与 delete的不足 。 这就要重载运算符 new与 delete,使其
按照要求完成对内存的管理 。
new和 delete只能被重载为类的成员函数, 不能重
载为友元 。 而且, 无论是否使用关键字 static进行修饰,
重载了的 new和 delete均为类的静态成员函数 。
运算符 new重载的一般形式为:
void *class_name::operator new( size_t,<arg_list>) ;
new重载应返回一个无值型的指针,且至少有一个
类型为 size_t的参数。若该重载带有多于一个的参数,
则其第一个参数的类型必须为 size_t。
运算符 delete重载的一般形式为:
void *class_name::operator delete( void *,<size_t>) ;
delete重载应返回一个无值型的指针,且至少有一
个类型为无值型指针的参数。该重载最多可以带有两
个参数,若有第二个参数,则其第二个参数的类型必
须为 size_t。
13.13 逗号运算符重载
逗号运算符是双目运算符,和其他运算符一样,
也可以通过重载逗号运算符来完成期望完成的工作。
逗号运算符构成的表达式为“左运算数,右运算数”,
该表达式返回右运算数的值。如果用类的成员函数来
重载逗号运算符,则只带一个右运算数,而左运算数
由指针 this提供。
例 13-12:程序中类 Point的 Point operator,( Point r)
重载逗号运算符成员函数返回参数 r对象的复制对象,
而 r对象就是逗号表达式的右操作数,这样也就返回了
右操作数,从而使重载后的逗号运算功能与其原有功
能接近。在计算 r1=( r1,r2+r3,r3);时,先计算
( r1,r2+r3),返回 r2+r3的结果,将其与 r3进行逗号
运算,返回 r3的结果。
13.10 类型转换运算符重载
类型转换运算符重载函数的格式如下:
operator <类型名 >( )
{
<函数体 >;
}
与以前的重载运算符函数不同的是,类型转换运
算符重载函数没有返回类型,因为 <类型名 >就代表了
它的返回类型,而且也没有任何参数。在调用过程中
要带一个对象实参。
实际上, 类型转换运算符将对象转换成类型名规
定的类型 。 转换时的形式就像强制转换一样 。 如果没
有转换运算符定义, 直接用强制转换是不行的, 因为
强制转换只能对标准数据类型进行操作, 对类类型的
操作是没有定义的 。
另外,转换运算符重载的缺点是无法定义
其类对象运算符操作的真正含义,因为只能进
行相应对象成员数据和一般数据变量的转换操
作。
13.1 运算符重载概述
13.2 运算符重载的实现
13.3 单目运算符重载
13.4 双目运算符重载
13.5 ->运算符重载
13.6 赋值运算符重载
13.7 下标运算符重载
13.8 运算符 new与 delete重载
13.13 逗号运算符重载
13.10 类型转换运算符重载
13.11 运算符重载应用实例
C++语言允许程序员重新定义已有的运算
符,使其能按用户的要求完成一些特定的操作,
这就是所谓的运算符重载。运算符重载与函数
重载相似,其目的是设置某一运算符,让它具
有另一种功能,尽管此运算符在原先 C++语言
中代表另一种含义,但他们彼此之间并不冲突。
C++会根据运算符的位置辨别应使用哪一种功
能进行运算。
13.1 运算符重载概述
运算符重载是对已有的运算符赋予多重含义, 同
一个运算符作用于不同类型的数据导致不同类型的行
为 。 C++中预定义的运算符的操作对象只能是基本数
据类型, 实际上, 对于很多用户自定义类型, 也需要
有类似的运算操作, 这就提出了对运算符进行重新定
义, 赋予已有符号以新功能的要求 。
运算符重载的实质就是函数重载。在实现过程中,
首先把指定的运算表达式转化为对运算符函数的调用,
运算对象转化为运算符函数的实参,然后根据实参的
类型来确定需要调用的函数,这个过程是在编译过程
中完成的。
C++可以重载的运算符如表 13-1所示 ;
C++不能被重载的运算符如表 13-2所示。
运算符重载的规则如下:
( 1) C++中的运算符除了少数几个以外, 全部可
以重载, 而且只能重载已有的这些运算符 。
( 2) 重载之后运算符的优先级和结合性都不会改
变 。
( 3)运算符重载是针对新类型数据的实际需要,
对原有运算符进行适当的改造。一般来讲,重载的功
能应当与原有功能相类似,不能改变原运算符的操作
对象个数,同时至少要有一个操作对象是自定义类型。
13.2 运算符重载的实现
运算符的重载形式有两种:重载为类的成员函数
和重载为类的友元函数 。
运算符重载为类的成员函数的语法形式如下:
<函数类型 > operator <运算符 >( <形参表 >)
{
<函数体 >;
}
friend <函数类型 > operator <运算符 >( <形参表 >)
{
<函数体 >;
}
其中, <函数类型 >指定了重载运算符的返回值类型,
operator是定义运算符重载函数的关键词, <运算符 >给
定了要重载的运算符名称, 是 C++中可重载的运算符,
形参表中给出重载运算符所需要的参数和类型 。 对于
运算符重载为友元函数的情况, 还要在函数类型说明
之前使用 friend关键词来说明 。
当运算符重载为类的成员函数时,函数的参数个
数比原来的运算数个数要少一个(后缀 ++,--除外);
当重载为类的友元函数时,参数个数与原运算数的个
数相同。
一般来讲,单目运算符最好重载为成员函数,而
双目运算符则最好重载为友元函数。运算符重载的主
要优点就是允许改变使用于系统内部的运算符的操作
方式,以适应用户新定义类型的类似运算。
13.3 单目运算符重载
类的单目运算符可重载为一个没有参数的非静态
成员函数或者带有一个参数的非成员函数, 参数必须
是用户字定义类型的对象或者是对该对象的引用 。
在 C++中, 单目运算符有 ++和 --,它们是变量自
动增 1和自动减 1的运算符 。 在类中可以对这两个单目
运算符进行重载 。
如同, ++”运算符有前缀, 后缀两种使用形式,
,++”和, --”重载运算符也有前缀和后缀两种运算符
重载形式, 以, ++”重载运算符为例, 其语法格式如
下:
<函数类型 > operator ++( ) ; //前缀运算
<函数类型 > operator ++( int); //后缀运算
使用前缀运算符的语法格式如下:
++<对象 >;
使用后缀运算符的语法格式如下:
<对象 >++;
使用运算符前缀时, 对对象 ( 操作数 ) 进行增量
修改, 然后再返回该对象 。 所以前缀运算符操作时,
参数与返回的是同一个对象 。 这与基本数据类型的运
算符前缀类似, 返回的也是左值 。
使用运算符后缀时,必须再增量之前返回原有的
对象值。为此,需要创建一个临时对象,存放原有的
对象,以便对操作数(对象)进行增量修改时,保存
最初的值。运算符后缀操作时返回的时原有对象值,
不是原有对象,原有对象已经被增量修改,所以,返
回的应该是存放原有对象值的临时对象。
13.4 双目运算符重载
对于双目运算符, 一个运算数是对象本身的数据,
由 this指针给出, 另一个运算数则需要通过运算符重载
函数的参数表来传递 。 下面分别介绍这两种情况 。
对于双目运算符 B,如果要重载 B为类的成员函数,
使之能够实现表达式,oprd1 B oprd2”,其中 oprd1为
A类的对象,则应当把 B重载为 A类的成员函数,该函
数只有一个形参,形参的类型是 oprd2所属的类型。经
过重载之后,表达式 oprd1 B oprd2就相当于函数调用
,oprd1.operator B( oprd2)”。
运算符也可以重载为类的友元函数,这样,它就
可以自由地访问该类的任何数据成员。这时,运算符
所需要的运算数都需要通过函数的形参表来传递,在
参数表中形参从左到右的顺序就是运算符运算数的顺
序。
13.5 ->运算符重载
“->”运算符是成员访问运算符, 这种一元的运算
符只能被重载为成员函数, 所以也决定了它不能定义
任何参数 。 一般成员访问运算符的典型用法是:
对象 ->成员
成员访问运算符, ->”函数重载的一般形式为:
type class_name::operator->( ) ;
13.6 赋值运算符重载
在 C++中有两种类型的赋值运算符:一类是, +=”
和, -=”等先计算后赋值的运算符, 另一类是, =”即直
接赋值的运算符 。 下面分别进行讨论 。
13.6.1 运算符, +=”和, -=”的重载
对于标准数据类型,,+=”和,-=”的作用是将一
个数据与另一个数据进行加法或减法运算后再将结果
回送给赋值号左边的变量中。对它们重载后,使其实
现其他相关的功能。
例 13-8, 程序中重载运算符,+=”和,-=”与标准
数据类型的,+=”和,-=”不完全相同。调用重载的运
算符时,例如 v1+=v2,并不改变 v1的值,而后者会改
变运算符左边变量的值。
13.6.2 运算符, =”的重载
赋值运算符, =”的原有含义是将赋值号右边表达
式的结果拷贝给赋值号左边的变量, 通过运算符, =”
的重载将赋值号右边对象的私有数据依次拷贝到赋值
号左边对象的私有数据中 。 在正常情况下, 系统会为
每一个类自动生成一个默认的完成上述功能的赋值运
算符, 当然, 这种赋值只限于由一个类类型说明的对
象之间赋值 。
如果一个类包含指针成员,采用这种默认的按成
员赋值,那么当这些成员撤消后,内存的使用将变得
不可靠。
可以重载运算符, =”来解决这个问题 。 重载该运
算符的成员函数如下:
Sample &operator = (Sample &s)
{
delete p;
p=new char[strlen(s.p)+1];
strcpy(p,s.p);
return *this;
}
把 s.p的内存复制到
this.p的内存中,而
不是仅仅进行指针复制
13.7 下标运算符重载
下标运算符, [ ]”通常用于在数组中标识数组元素
的位置, 下标运算符重载可以实现数组数据的赋值和
取值 。 下标运算符重载函数只能作为类的成员函数,
不能作为类的友元函数 。
下标运算符, [ ]”函数重载的一般形式为:
type class_name::operator[ ]( arg_) ;
其中 arg为该重载函数的参数。重载了的下标运算
符只能且必须带一个参数,该参数给出下标的值。重
载函数 operator[ ]的返回值类型 type是引用类型。
13.8 运算符 new与 delete重载
C++提供了 new与 delete两个运算符用于内存管理,
但有些情况下用户需要自己管理内存, 为自己所定义
的类体系建立一种新的动态内存管理算法, 以克服 new
与 delete的不足 。 这就要重载运算符 new与 delete,使其
按照要求完成对内存的管理 。
new和 delete只能被重载为类的成员函数, 不能重
载为友元 。 而且, 无论是否使用关键字 static进行修饰,
重载了的 new和 delete均为类的静态成员函数 。
运算符 new重载的一般形式为:
void *class_name::operator new( size_t,<arg_list>) ;
new重载应返回一个无值型的指针,且至少有一个
类型为 size_t的参数。若该重载带有多于一个的参数,
则其第一个参数的类型必须为 size_t。
运算符 delete重载的一般形式为:
void *class_name::operator delete( void *,<size_t>) ;
delete重载应返回一个无值型的指针,且至少有一
个类型为无值型指针的参数。该重载最多可以带有两
个参数,若有第二个参数,则其第二个参数的类型必
须为 size_t。
13.13 逗号运算符重载
逗号运算符是双目运算符,和其他运算符一样,
也可以通过重载逗号运算符来完成期望完成的工作。
逗号运算符构成的表达式为“左运算数,右运算数”,
该表达式返回右运算数的值。如果用类的成员函数来
重载逗号运算符,则只带一个右运算数,而左运算数
由指针 this提供。
例 13-12:程序中类 Point的 Point operator,( Point r)
重载逗号运算符成员函数返回参数 r对象的复制对象,
而 r对象就是逗号表达式的右操作数,这样也就返回了
右操作数,从而使重载后的逗号运算功能与其原有功
能接近。在计算 r1=( r1,r2+r3,r3);时,先计算
( r1,r2+r3),返回 r2+r3的结果,将其与 r3进行逗号
运算,返回 r3的结果。
13.10 类型转换运算符重载
类型转换运算符重载函数的格式如下:
operator <类型名 >( )
{
<函数体 >;
}
与以前的重载运算符函数不同的是,类型转换运
算符重载函数没有返回类型,因为 <类型名 >就代表了
它的返回类型,而且也没有任何参数。在调用过程中
要带一个对象实参。
实际上, 类型转换运算符将对象转换成类型名规
定的类型 。 转换时的形式就像强制转换一样 。 如果没
有转换运算符定义, 直接用强制转换是不行的, 因为
强制转换只能对标准数据类型进行操作, 对类类型的
操作是没有定义的 。
另外,转换运算符重载的缺点是无法定义
其类对象运算符操作的真正含义,因为只能进
行相应对象成员数据和一般数据变量的转换操
作。