第11章 友元与运算符重载 ·280·
类A是类B的友元,类B是类C的友元,但类A并不是类C的友元。 (2)友元关系不具有交换性
类A是类B的友元,但类B并不一定是类A的友元。 (3)友元关系是不能继承的
例如:函数f()是类A的友元,类A派生出类B,函数f()并不是类B的友元。除非在类B中作了特殊说明。
11.2 运算符重载
11.2.1 运算符重载的概念
为了介绍运算符重载的概念,先看一个运算符重载的引例。
引例 用“+”运算符完成两个实数、两个复数、两个字符串的相关运算。 (1)实数
设有两个实数:x1=10 ,x2=20,则两个实数相加的结果是:x1+x2=10+20=30。 (2)复数
设有两个复数:x1=10+10i,x2=20+20i,则两复数相加结果是:x1+x2=30+30i 。 (3)字符串
设有两个字符串:x1=\,x2=\,则两字符串连接的结果:
x1+x2=\
由上例可以看出,同一个运算符“+”可用于完成实数加法、复数加法及字符串连接等不同的运算,得到完全不同的结果。这就是“+”运算符的重载。因此,所谓运算符重载就是用同一个运算符完成不同的运算操作。在C++这类面向对象的程序设计语言中,运算符重载可以完成两个对象的复杂操作(如:两个复数的算术运算等)。而运算符重载是通过运算符重载函数来完成的。当编译器遇到重载运算符,如复数加法:x1+x2 中的加号运算符“+”时,自动调用“+”运算符的重载函数完成两个复数对象的加法操作。由于二元运算符的重载函数与一元运算符的重载函数有较大区别,因此分开介绍。先介绍二元运算符重载函数,再介绍一元运算符重载函数。
11.2.2 二元运算符重载函数
运算符重载函数通常是类的成员函数或者是友元函数,下面先介绍运算符重载函数为类的成员函数的定义格式及使用方法,然后介绍运算符重载函数为友元函数的定义格式及使用方法。
1.运算符重载函数为类的成员函数
运算符重载函数为类的成员函数一般定义格式为: <类型><类名>::
其中,类型为运算符重载函数的返回类型。类名为成员函数所属类的类名。关键词“operator”加上“重载运算符”为重载函数名,即:重载函数名= operator重载运算符。形参常为参加运算的对象或数据。
第11章 友元与运算符重载 ·281·
【例11.4】定义一个复数类,重载“=”运算符,使这个运算符能直接完成复数的赋值运算。
# include
float Real,Image; public:
Complex(float r=0,float i=0)
{ Real=r;Image=i;} // 缺省构造函数
void Show(int i) //显示输出复数
{ cout<<\
void operator =(Complex &c) //“=”运算符重载函数完成复数赋值操作 { Real=c.Real; Image=c.Image; } };
void main(void)
{ Complex c1(25,50),c2; c1.Show(1);
c2=c1;
c2.Show(2); }
程序执行后输出:
c1=25+50i c2=25+50i
在程序中,定义了一个赋值运算符“=”的重载函数:
void operator =(Complex &c) { Real=c.Real; Image=c.Image; }
该重载函数的函数名为“operator =”,返回类型为void,形参为复数类对象的引用Complex &c。当程序执行主函数中的赋值语句c2=c1而遇到赋值运算符“=”时,自动调用赋值运算符“=”重载函数“operator =()”,并将“=” 运算符右边的操作数c1作为实参,左边操作数c2作为调用重载函数的对象,即:作了一次 c2.operator=(c1) 的函数调用。在函数的调用过程中,实参c1传给形参c,在函数体内完成了复数实部与虚部的赋值操作:
Real=c1.Real; Image=c1.Image;
因为重载函数是复数对象c2的成员函数,所以上式中复数的实部Real与虚部Image为c2的实部c2.Real与虚部c2.Image。因此,上式是将复数对象c1的实部与虚部赋给复数对象c2的实部与虚部。即完成了两个复数的赋值工作。
第11章 友元与运算符重载 ·282·
【例11.5】在例11.4定义复数类中,重载“+”、“-”运算符,使这二个运算符能直接完成复数的加、减运算。
# include
float Real,Image; public:
Complex(float r=0,float i=0) { Real=r;Image=i;}
void Show(int i) //显示输出复数
{ cout<<\
Complex operator + (Complex &c); //“+”运算符重载函数完成两个复数加法 Complex operator - (Complex &c); //“-”运算符重载函数完成两个复数减法 Complex operator + (float s); //“+”运算符重载函数完成复数实部加实数 void operator +=(Complex &c); //“+=”运算符重载函数完成复数=复数+c void operator =(Complex &c); //“=”运算符重载函数完成两个复数赋值 };
Complex Complex::operator + (Complex &c) { Complex t;
t.Real=Real+c.Real; t.Image=Image+c.Image; return t; }
Complex Complex::operator-(Complex &c) { Complex t;
t.Real=Real-c.Real; t.Image=Image-c.Image; return t; }
Complex Complex::operator + (float s) { Complex t; t.Real=Real+s; t.Image=Image; return t; }
void Complex::operator += (Complex &c) { Real=Real+c.Real;
Image=Image+c.Image; }
第11章 友元与运算符重载
void Complex::operator = (Complex &c) { Real=c.Real; Image=c.Image; }
void main(void)
{ Complex c1(25,50),c2(100,200) ,c3,c4; c1.Show(1); c2.Show(2); c3=c1+c2; //c3=(25+50i)+(100+200i)=125+250i c3.Show(3); c4=c2-c1; //c4=(100+200i)-(25+50i)=75+150i c4.Show(4); c2+=c1; c2.Show(2); c1=c1+200; c1.Show(1); }
执行程序后输出:
c1=25+50i c2=100+200i c3=125+250i c4=75+150i c2=125+250i c1=225+50i
图11.1 c3=c1+c2的运算
·283·
//c2=c2+c1= (100+200i)+ (25+50i)=125+250i //c1=(25+50i)+200=225+50i
对象c3 Real=125 Image=250
对象c1 Real=25 Image=50
对象c2 Real=100 Image=200
= +
在上例中重载了运算符“+”、“-”“+=”、“=”,可以实现复数的加法、减法、赋值等操作。从主函数中可以看出,经重载后运算符的使用方法与普通运算符一样方便。如复数c1加c2赋给c3的加法运算:c3=c1+c2与普通实数加法形式上完全相同,如图11.1所示。但实际执行过程中确是完全不同的。实现复数加法运算是通过调用加法运算符重载函数来完成,而对加法运算符重载函数的调用是由系统自动完成的。如主函数中表达式:c3=c1+c2;编译器先将c1+c2解释为对“+”运算符重载函数:c1.operator+(c2) 的调用。再将该表达式解释为对“=”运算符重载函数:c4.operator=(c1.operator+(c2))的调用。由c1.operator+(c2)成员函数求出复数c1+c2的值t,并返回一个计算结果t,然后再由成员函数c3.operator=(t),完成复数c3=t的赋值运算,将运算结果赋给c3。
对于运算符重载,必须说明以下几点:
(1)运算符重载函数名必须为:operator <运算符>
(2)运算符的重载是通过调用运算符重载函数实现的,调用函数时,左操作数为调用重载函数的对象,右操作数作为函数的实参,实参可以是对象、实数等其它类型。
(3)形参说明
若重载函数为成员函数,则参加二元运算的左操作数为调用重载函数的对象。因此,
第11章 友元与运算符重载 ·284·
重载函数为成员函数的参数通常为一个,即右操作数。如在上例中,二元加法运算:c1+c2被解释为对重载成员函数c1.operator+(c2)的调用,此时重载函数只有一个参数。
(4)运算符重载函数的返回类型
若二个同类对象进行二元运算后的结果类型仍为原类型,则运算符重载函数的返回类型应为原类型。如在上例中,由于两个复数运算的结果仍为复数,因此上述运算符重载函数的返回类型均为复数类型Complex。
(5)C++中允许重载的运算符如下表11.1所示:
表11.1 C++中允许重载的操作符 + ~ ++ += <<= - ! -- -= >>= * , << *= [ ] / = >> /= ( ) % < = = %= -> ^ > != ^= ->* & <= && &= new | >= || |= delete (6)C++中不允许重载的运算符如表11.2所示:
表11.2 C++中不允许重载的运算符 运算符 ?: · * :: sizeof 运算符的含义 三目运算符 成员操作符 成员指针操作符 作用域运算符 求字节数操作符 不允许重载的原因 在C++中没有定义一个三目运算符的语法 为保证成员操作符对成员访问的安全性,故不允许重载 同上 因该操作符左边的操作数是一个类型名,而不是一个表达式 其操作数是一个类型名,而不是一个表达式 (7)只能对C++中已定义了的运算符进行重载,当重载一个运算符时,该运算符的优先级和结合律是不能改变的。
2.运算符重载函数为友元函数
运算符重载函数为友元函数的一般定义格式为: <类型>
其中,类型为运算符重载函数的返回类型。operator<重载运算符>为重载函数名。形参1与形参2常为参加运算的两个对象的引用。对于形参需说明如下:
当重载函数为友元普通函数时,该重载函数不能用对象调用,因此参加运算的二个对象必须以形参方式传送到重载函数体内,所以运算符重载函数为友元函数时,形参通常为二个参加运算的对象。
【例11.6】用友元运算符重载函数实现复数的加、减运算。
# include
float Real,Image; public: