贵州大学计算机学院Visual C++maocpp5_第1页
贵州大学计算机学院Visual C++maocpp5_第2页
贵州大学计算机学院Visual C++maocpp5_第3页
贵州大学计算机学院Visual C++maocpp5_第4页
贵州大学计算机学院Visual C++maocpp5_第5页
已阅读5页,还剩128页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第五章多态性

1

5.1编译时的多态性与运行时的多态性

多态性是指用同一个名字定义不同的函数,这些函数执行不同但

又类似的操作。

联编的概念:

一个源程序经过编译、连接、成为可执行文件的过程是把可执行

代码联编(或称装配)在一起的过程。

静态联编(前期联编)

’静态联编要求在程序编译时就知道调用哪个函数,就决定如何

实现某一动作。

动态联编(后期联编、滞后联编)

一直要到程序运行时才能确定调用哪个函数。系统在运行时才动

态完成的联编。

静态联编支持的多态性称为编译时多态性,也称静态多态性。在

C++中,编译时多态性是通过函数重载和运算符重载实现的。

动态联编支持的多态性称为运行时多态性,也称动态多态性。在

C++中,运行

时多态性是通过继承和虚函数来实现的。2

5.2函数重载I<

质译时多本件可以通过函数重载来实现。函数重载的意义在于它能

Voidmain()

{pointp(20,20);

circlec(8,8,30);

cout«p.area()«endl;〃执彳亍基类point中的area()的函数

cout«c.area()«endl;〃执彳亍派生类circle中的areaO函数

coutvvc.point::area()vvendl;〃执彳亍基类point中的areaO的函数

}

程序运行结果为:

0

2827.439941

0

classcircle:publicpoint(

intradius;

public:

circle(intx,inty,intrad):point(x,y){radius=rad;}

floatarea(){return34416*radius*radius派生类中Marea函数

0・3

说明:<

在基类和派生类中的函数重载有两种情况:

1.参数有所差别的重载,这种重载函数的定义和调用方法在前面章

节已进行了介绍;在编译时根据参数不同来区分函数重载。

2.函数所带的参数完全相同,只是它们属于不同的类。这时要人工

干预,以下两种方法可以使编译时区别这类函数:

(1)使用对象名加以区分。例如:p.area()和c.erea()分别

调用类point的area()函数和类circle的area()函数。

(2)使用“类名::''加以区分。例如:point::area()和

circle::area()分别调用类point和类circle的area()函数。

4

5.3运算符重载<

在C++中,除了可以对函数重载外,还可以对大多数运算

符实施重载。

运算符重载通过创建运算符函数operator()来实现。

运算符函数定义了重载的运算符将要进行的新的操作,

这种操作通常作用在一个类上。

函数operatorO可以

(1)外部函数

(2)类的友元函数

(3)是类的成员函数

5

5.3.1类以外的运算符重载<

对基本的数据类型,C++提供了许多预定义的运算符,如“+”、”-

工“二,,等,若有一个复数类complex:

classcomplex{

public:

doublereal,imag;

complex(doubler=0,doublei=0){real=r;imag=i;}

};

若要把类complex的两个对象com1和com2加在一起,下面的语句

是不能实现的:

voidmain()

(

complexcom1(1.1,2.2),com2(3.3,4.4),total;

total=com1+com2;〃错误

〃・・・

}

错误原因是类complex的类型不是基本数据类型,而是用户自定义

的数据类型。C++还是无法直接将两个complex类对象相加。

类外部的运算符函数外

为了表达上的方便,人们希望预定义的内部运算符(如“+”、

“*”、等)在特定类的对象上以新的含义进行解释,如希

望能够实现tota仁com1+com2,这就需要用重载运算符来解决。

C++为运算符重载提供了一种方法,即在进行运算符重载时,

必须写一个运算符函数,其名字为operator后随一个要重载的运

算符。例如,要重载号,应该写一个名字为operator+的

函数。

---表5.1

函数功△*匕

operator+()法

operator-()

operator*()

operator/()

operator<()

7

运算符函数operator+()

在编译时遇到名为operator@的运算符函数(@表示所要重

算符),就检查传递给函数的参数的类型。如果编译器在一个运

算符的两边看到自定义的数据类型,就执行用户自己的函数,而

不是常规运算符。

若要将上述类complex的两个对象相加,只要写一个运算符函数

operator+O

complexoperator+(complexom1,complexom2)

(

complextemp;

temp.real=om1.real+om2.real;

temp.imag=om1.imag+om2.imag;

returntemp;

}

我们就能方便的使用语句total=com1+com2;

将类complex的两个对象com1和com2相力口。

也可以使用以下的调用语句,将两个complex类对象相加:

total=operator+(com1,com2);

这两个调用语句是等价的,但显然后者不如前者简明和方便。

例5.2运算符函数operator+()将两个compIex类对象相加程序

#include<iostream.h>

classcomplex{

public:

doublereal;

doubleimaa:

Voidmain()

{complexcom1(1.1,2.2),com2(3.3,4.4),total1,total2;

total1=operator+(com1,com2);〃调用运算符函数operator+O

的第一种方式

cout«"real1="«total1.reaK<5

,,«"imag1="«total1.imag«endl;

total2=com1+com2;〃调用运算符函数operator+()的第二种方

coutvv"real2="vvtotal2,realvv”

,,«"imag2="«total2.imag«endl;

)

程序运行结果为:

real1=4.4imag1=6.6

reaI2=4.4imag2=6.6

综合说明:<

(1)运算符重载函数operator@O可以返回任何类型,甚至可以是void类型

但通常返回类型与它所操作的类的类型相同,这样可以使重载运算符用在复

杂的表达式中。

(2)在重载运算符时,运算符函数所作的操作不一定要保持C++中该运算符原

有的含义。例如,可以把加运算符重载成减操作,但这样容易造成混乱。所

以保持原含义,容易被接受,也符合人们的习惯。

(3)在C++中,用户不能定义新的运算符,只能从C++已有的运算符中选择一

个恰当的运算符重载。

(4)C++编译器根据参数的个数和类型来决定调用哪个重载函数。因此,可以

为同一个运算符定义几个运算符重载函数来进行不同的操作

(5)重载运算符与预定义运算符的使用方法完全相同,它不能改变原有运算符

的参数个数(单目或双目),也不能改变原有的优先级和结合性。

(6)在C++中,大多数系统预定义的运算符都能被重载,例如

*

+J.,*1/0%/A&©I1

■1=<>+=・*=

/=%=A=&=1=«»»=

«===1■=<=>=&&II++

■■[]0newdelete

也有一些运算符是不能被重载的,如:

*..Q>

前处如符号痴##也不露重载。

5.3.2友元运算符函数I<

把运算符函数定义成某个类的友元函数,称为友元运算符

函数。

1.友元运算符函数定义的语法形式

友元运算符函数在类的内部声明格式如下:

classX{

//.....

friend返回类型operator运算符(参数表);

//.....

);

11

在类外,友元运算符函数与定义一般的友元函数相似,其

格式如下:

返回类型operator运算符(参数表)

〃函数体

}

由于友元函数不是类的成员函数,所以不需要缀上类名。

与成员运算符函数不同,友元运算符函数是不属于任何

类对象的,它没有this指针。若重载的是双目运算符,

则参数表中有两个操作数;若重载的是单目运算符,则

参数表中只有一个操作数。

12

2.双目运算符重载

当用友元函数重载双目运算符时,两个操作数都要传给运

算符函数。

例5.3友元函数重载双目运算符

数a+bi和c+di进行加、减、乘、除的方法如下:

(a+bi)+(c+di)=(a+c)+(b+d)i

法(a+bi)-(c+di)=(a-c)+(b-d)i

除(a+bi)*(c+di)=(ac-bd)+(ad+bc)i

(a+bi)/(c+di)=((a+bi)*(c-di))/(c*c+d*d)

13

#include<iostream.h>

classcomplex{

private:

doublereal;

doubleimag;

public:

complex(doubler=0.0,double1=0.0){real=r;imag=i;}

voidprint();

friendcomplexoperator+(complexa,complexb);

//相友元运算符函数重载复数

friendcomplexoperator-(complexa,complexb);

〃用友元运算符函数重载鱼数

friendcomplexoperator*(complexa,complexb);

〃用友元运算符函数重载复数

friendcomplexoperator/(complexa,complexb);

〃用友元是算符函薮重载复数

);

14

接1例5.3

complexoperator+(complexa,complexb)〃重载“十”定义

{complextemp;

temp.real=a.real+b.real;temp.imag=a.imag+b.imag;

returntemp;

}

complexoperator-(complexa,complexb)〃重载定义

{complextemp;

temp.real=a.real-b.real;temp.imag=a.imag-b.imag;

returntemp;

)

complexoperator(complexa,complexb)〃重载定义

{complextemp;

temp.real=a.rearb.real-a.imag*b.imag;

temp.imag=a.rearb.imag+a.imag*b.real;

returntemp;

)15

接2例5.5

complexoperator/(complexa,complexb)〃重载定义

{complextemp;

doublet;

t=1/(b.rearb.real+b.imag*b.imag);

temp.real=(a.rearb.real+a.imag*b.imag)*t;

temp.imag=(b.reara.imag-a.real*b.imag)*t;

returntemp;

}

voidcomplex::print()〃输出显示复数

(

cout«real;

if(imag>0)cout«u+,5;

if(imag!=0)cout«imag«^^i\n^^

}16

voidmain()

{complexAl(2,3,4.6),A2(3,6,2.8),

A3,A4,A5,A6;〃定义6个复数对象

A3=A1+A2;〃复数相加

A4=A1-A2;〃复数相减

A5=A1*a2;〃复数相乘

A6=A1/A2;〃复数相除

A1.print();〃输出复数A1

A2.print();〃输出复数A2

A3.print();〃输出复数相加结果A3

A4.print();〃输出复数相减结果A4

A5.print();〃输出复数相乘结果A5

A6.print();〃输出复数相除结果A6

17

程序运行结果如下:

2.3+4.6i

3.6+2.8i

5.9+7.4i

13+18

-4.6+23i

1.017308+0.486538i

调用时:

aa@bb;//隐式调用

opreator@(aa,bb)〃显式调用

18

3.单目运算符重载<

用友元函数重载单目运算符时,需要一个显式的操作数。

例5.4用友元函数重载单目运算符“-”例.子。,

#include<iostream.h>voidmian()

classAB{{ABob1(50,60),ob2;

inta,b;ob1.show();

public:ob2="ob1;

AB(intx=0,inty=0){a=x;b=y;}ob2.show();

friendABoperator-(ABobj);〃声}、一、_

voidshow();

);

ABoperator-(ABob|)〃定义重载单目运算符

{obj.a=-obj.a;obj.b=-obj.b;returnobj;}

voidAB::show()

{cout«ua,,,,«au<,,=,,«b«end}

程序运行结果如下:

a=50b=60

3=-50b=-6019

仗m汉/L四姒里秋丁丁.,-------心q十口^心舁1可,HJ日匕云可以心口IT

喻5.5

ttinclude<iostream.h>

classcoord{

intx,y;

public:

coord(inti=0,intj=0){x=i;y=j;}

voidprint(){cout<<〃x:〃〈〈x〈〈〃,y:〃〈〈y〈〈endl;}

friendcoordoperator++(coordop);

);

coordoperator++(coordop)

{++op.X;

++op.y;

returnop;

20

voidmain()

{coordob(10,20);

ob.print();

operator++(ob);//显式调用

ob.print();

++ob;//隐式调用

ob.print();

)

运行结果:

x:10,y:20

x:10,y:20

x:10,y:20

这个运行结果没有达“++”的目的,由于友元函数没有this指针,

所以不能引用this指针所指的对象。这个函数是通过传值的方法

传递参数的,函数体内对。P的所以修改都无法传到函数体外。因

止匕在operator++函数中,任何内容的改变不会影响产生调用的

操作数,也就是说。实际上对象x和y并未增加。21

为了解决以上问题,使用友元函数重载单目运算符“++”匚力

“一”时,应采用引用参数传递操作数(&),这样函数参数的任何改

变都影响产生调用的操作数,从而保持了两个运算符的原意。

例5.6使用友元运算符函数重写例5.5的程序。

#include<iostream.h>

classcoord{

intx,y;

public:

coord(inti=O,intj=0){x=i;y=j;}

voidprint(){cout«ux:"«x«u,y:^«y«endl;}

friendcoordoperator++(corrd&op);

〃声明友元运算符函数,形参为引用

};

coordoperator++(coord&op)〃定义友元运算符函数

{++op.x;

++op.y;

returnop;

}22

voidmain()

{coordob(10,20);

ob.print();

++ob;〃隐式调用友元运算符函数

ob.print();

operator++(ob);〃显式调用友元运算符函数

ob.print();

)

程序运行的结果如下:

x:10,y:20

x:11,y:21

x:12,y:22

23

当用友元函数重载单目运算符时,参数表中有一个操作数

一般而言,采用友元函数重载单目运算符@后,可以采用以下

两种方法来使用:

@aa;〃隐式调用

operator@(aa);〃显式调用

综合说明:

(1)不能用友元函数重载的运算符是:=,(),[],・>其余的

运算符都可以使用友元函数来实现重载。

(2)由于单目运算符“•”可不改变操作数自身的值,所以在例5.4

载单目运算符的友元运算符函数的原型可写成:

friendABoperator-(ABobj);

通过传值的方式传递参数。

24

5.3.3成员运算符函数<

运算符函数可以定义为类的成员(称为成员运算符函数)

1.成员运算符函数定义的语法形式

成员运算符函数的原型在类的内部声明格式为:

classX{

//...

返回类型operator运算符(形参表);

//...

};

类外成员运算符函数定义的更一般形式为:

返回类型X:operator运算符(形参表)

//函数体

}

在成员运算符函数的参数表中,若运算符是单目的,则参

数表为空;若运算符是双目的,则参数表中有一个操作

数。

2.双目运算符重载<

对双目运算符而言,成员运算符函数的参数表中仅有一个

参数,它们为运算符的右操作数,此时当前对象作为运

算符的左操作数,它们通过this指针隐含地传递给函数

的。例如:

classX{

〃…

intoperator+(Xa);

);

在类X中声明了重载“十”的成员运算符函数,返回类型为

int,它具有两个操作数,一个是当前对象,另一个是

对象a。

例5.7用双目运算符函数进行复数运算的例子

26

#include<iostream.h>

classcomplex{

private:

doublereal;〃复数的实数部分

doubleimag;〃复数的虚数部分

public:

complex(doubler=0.0,doublei=0.0);〃构造函数

voidprint();〃显示输出复数

complexoperator+(complexc);〃重载复数运算

complexoperator-(complexc);〃重载复数“一”运算

complexoperator*(complexc);〃重载复数“钎运算

complexoperator/(complexc);〃重载复数运算

);27

complex::complex(doubler,doublei)〃定义构造函数

接1例5.7

complexcomplex::operator+(complexc)〃重载定义

complextemp;

temp.real=real+c.real;

temp.imag=imag+c.imag;

returntemp;

)

complexcomplex::operator-(complexc)〃重载定义

(

complextemp;

temp.real=real-c.real;

temp.imag=imag-c.imag;

returntemp;

)

28

接2例5.7

complexcomplex::operator*(complexc)〃重载“*F

(

complextemp;

temp.real=real*c.real-imag*c.imag;

temp.imag=imag*c.real+rearc.imag;

returntemp;

)

complexcomplex::operator/(complexc)〃重载定义

(

complextemp;

doublet;

t=1/(c.rearc.real+c.imag*c.imag);

temp.real=(rearc.real+imag*c.imag)*t;

temp.imag=(c.rearimag-rearc.imag)*t;

returntemp;

)29

接3例5.7

voidcomplex::print()〃显示复数的实数部分和虚g

{cout«real;

if(imag>0)cout«,,+";

if(imag!=0)cout«imag«"i\n";

)

voidmain()

{complexA1(2.3,4.6),A2(3.6,2.8),A3,A4,A5,A6;//定义六个复

数类对象

A3=A1+A2;〃复数相加

A4=A1-A2;〃复数相减程序运行结果如下:

A5=A1*A2;〃复数相乘2.3+4.6i

A6=A1/A2;〃复数相除3.6+2.8i

A1.print();〃输出复数A15.9+7.4i

A2.print();〃输出复数A2-1.3+1.8i

A3.print();〃输出复数相加的结身

-4.6+23i

A4.print();〃输出复数相减的结身1017308+0486„8i

A5.print();〃输出复数相乘的结果!-017308+0.4865381

A6.print();〃输出复数相除的结果A6

30

}

在主函数main()中的语句|.

A3=A1+A2;一—

A4=A1-A2;

A5=A1*A2;

A6=A1/A2;

所用的运算符“+”、"-”、“*"、“/”是重载后的运算符。程序

执行到这四条语句时C++将其解释为:

A3=A1.operator+(A2);

A4=A1.operator-(A2);

A5=A1.operator*(A2);

A6=A1.operator/(A2);

由此我们可以看出,成员运算符函数operator@实际上是由双目

运算符左边的对象A1调用的,尽管双目运算符函数的参数表只

有一个操作数A2,但另一个操作数是由对象A1通过this指针隐

含地传递的。以上两组语句的执行结果是完全相同的。一般而言,

采用成员函数重载双目运算符@后,可以用以下两种方法来使用:

aa@bb;〃隐式调用

aa.operator@(bb);〃显式调用

成员运算符函数operator@所需的一个操作数由对象aa通过this

指针隐含地传递。因此,它的参数表中只有一个操作数bbB

3.单目运算符重载<

对单目运算符而言,成员运算符函数的参数表中没有参数,此时当

前对象作为运算符的一个操作数。

例5.8重载单目运算符“++”的例子

#include<iostream.h>

classcoord{

intx,y;

public:

coord(inti=O,intj=0);

voidprint();

coordoperator++();〃声明成员运算符函数operator++()

);

coord::coord(inti,intj){x=i;y=j;}

voidcoord::print(){cout«"x:"<<x«",y:"«y«endl;}

coordcoord::operator++()〃定义成员运算符函数。perator++()

{++x;

++y;

return*this;

)

32

接1例5.8

voidmain()

{coordob(10,20);

ob.print();

++ob;〃隐式调用成员运算符函数operator++()

ob.print();

ob.operator++();〃显式调用成员运算符函数operator++()

ob.print();

)

程序运行结果如下:

x:10,y:20

x:11,y:21

x;12,y=22

不例主函数main()中调用成员运算符函数operator++()采用

了两种方法:++ob;与ob.operator++()

两者是等价的,其执行效果是完全相同的。

从本例还可以看出,当成员函数重载单目运算符时,没有参数被显

式地传递给成员运算符函数。参数是通过this指针隐含地传递给

函数的。33

采用成员函数重载单目运算符时,可以用以下两种方法来使用:

@aa;〃隐式调用

aa.operator@();〃显式调用

成员运算符函数operator@所需的一个操作数由对象aa通过this

指针隐含地传递。因此,在它的参数表中没有参数。

34

5.3.4成员运算符函数与友元运算符函数的比较

我们对成员运算符函数与友元运算符函数作一比较。

(1)对双目运算符,成员运算符函数带有一个参数,友元运算符函数

带有两个参数对单目运算符,成员运算符函数不带参数,友元运算

符函数带有一个参数。

(2)双目运算符一般可以被重载为友元运算符函数或成员运算符函

数,但有一种情况,必须使用友元函数。

例如,在例5.4的类AB中,用成员运算符函数重载运算

ABAB::operator+(intx)

ABtemp;

temp.a=a+x;

temp.b=b+x;

returntemp;

35

若类AB的对象ob要做赋值运算和加法运算,以下是一条正确的语句

ob=ob+100;

由于对象ob是运算符的左操作数,所以它调用了运算符重

载函数,把一个整数加到了对象ob的某些元素上去。然而,下一条

语句就不能工作了:

ob=100+ob;

不能工作的原因是运算符的左操作数是一个整数,而整数是一个内

部数据类型,100不能产生对成员运算符函数的调用。

36

用两种形式的友元函数来重载运算符函数“+”

就能消除由于运算符的左操作数是内部数据类型而带来的问题,

下述程序说明了如何实现。

例5.9用两种形式的友元函数来重载运算符函数

#include<iostream.h>

classAB{

inta,b;

public:

AB(intx=O,inty=0){a=x;b=y}

friendABopterator+(ABob,intx);

〃声明一种友元运算符函数

friendABopterator+(intx,ABob);

〃声明另一种友元运算符函数

voidshow();

);

37

接1例5.9

ABoperator+(ABob,intx)

〃定义常规类型在右边的友元运算符函数

{ABtemp;

temp.a=a+x;

temp.b=b+x;

returntemp;

)

ABoperator+(intx,ABob)

〃定义常规类型在左边的友元运算符函数

ABtemp;voidmain()

temp.a=x+ob.a;{ABob1(30,40),ob2;

temp.b=x+ob.b;

ob2=ob1+30;

returntemp;ob2.show();

)ob2=50+ob1;

voidAB::show()

,,,ob2.show();

{coutvv"a=''vvavv''u«b=<<b<<k}

(3)成员运算符函数和友元运算符函数都可以用习惯方式调—

也可以用它们专用的方式调用,表5.2列出了一般情况下运算符

函数的调用形式。

习惯形式友元运算符函数调用形式成员运算符函数调用形式

a+boperator+(a,b)a.operator+(b)

-aoperator-(a)a.operator-()

a++operator++(a,0)a.operator++(0)

(4)C++的大部分运算符既可说明为成员运算符函数,又可说明

为友元运算符函数。究竟选择哪一种运算符函数好一些,没有定

论,这主要取决于实际情况和程序员的习惯。

一般而言,对于双目运算符,将它重载为一个友元运算符函数

比重载为一个成员运算符函数便于使用。若一个运算符的操作需

要修改类对象的状态,则选择成员运算符函数较好。如果运算符

所需的操作数(尤其是第一个操作数)希望有隐式类型转换,则

运算符重载必须用友元函数,而不能用成员函数。39

有以下的经验:

•对于单目运算符,建议选择成员函数;

•对于运算符J、()、[]、-〉”只能作为成员函数;

•对于运算符“+二、一二、/二、*二、&=>!=>〜二、%二、>>=>

〈〈二”,建议重载为成员函数;

•对于其他运算符,建议重载为友元函数。

40

5.3.5“++”和“--”的重载<

运算符“++”和“--”放置在变量的前面与后面,其作用是有区别

的。但是C++2.1之前的版本在重载“++”(或“--”)时,不能

显式地区分是前缀还是后缀方式。也就是说,在例5.8的main()

函数中,以下两条语句是完全相同的:

ob++;++ob;

在C++2.1及以后的版本中,编辑器可以通过在运算符函数参数表中

是否插入关键字int来区分这两种方式。

(1)前缀方式++ob,可以用运算符函数重载为:

ob.operator++();//成员函数重载

operator++(X&ob);//友元函数重载,其中ob为X类对象

⑵后缀方式ob++,可以用运算符函数重载为:

ob.operator++(int);〃成员函数重载

operator++(X&ob,int);〃友元函数重载

调用时,参数int一般用0值。

重载运算符“--”也可用类似的方法。

41

例5.10包含“++”和“一”两种重载运算符

#include<iostream.h>

#include<iomanip.h>

classover{

inti1,i2,i3;

public:

voidinit(int11,intI2,intI3){i1=l1;i2=l2;i3=l3;}

〃给数据成员赋初值

voidprint();{cout«ui1:"vvilvv“i2:"vvi2vv”

i3:«i3«endl;〃显示输出数据成员

overoperator++();〃成员函数重载“++”(前缀方式)声

overoperator++(int);//成员函数重载“++”(后缀方式)声

friendoveroperator--(over&);

〃友元函数重载(前缀方式)声

42

friendoveroperator--(over&,int);

接1例5.10<

overover::operator++()〃定义成员函数重载(前缀方式)

{++i1;++i2;++i3;

return*this:

)

overover::operator++(int)〃定义成员函数重载++”(后缀方式)

{i1++;i2++;i3++;

return*this;

)

overoperator--(over&op)〃定义友元函数重载(前缀方

式)

{--op.il;--op.i2;--op.i3;

returnop;

}

overoperator--(over&op,int)//定义友元函数重载(后缀方

式)

{op.i1--;op.i2-op.i3--;43

returnop;

接2例5.10

Voidmain()

{overobj1,obj2,obj3,obj4;

nhi125V

程序运行结果如下:

i1:5i2:3i3:6

i1:3i2:6i3:10

i1:7i2:2i3:7

i1:2i2:5i3:6

i2:4i3:7

i2:7i3:11

i2:1i3:6

i2:4i3:5

以上例子中,使用成员函数分别以前缀方式和后缀方式重载了运算

符“十+”,使用友元函数分别以前缀方式和后缀方式重载了运算符

ODJi.pnnqj;oojz.pnnq);oojo.pnnq);ooj4.prinq);44

}

5.3.6赋值运算符“二”的重载<

对任一类X,如果没有用户自定义的赋值运算符函数,那么系统将

自动地为其生成一个默认的赋值运算符函数,例如:

X&X::operator=(constX&source)

(

〃成员间赋值

)

若obj1和obj2是类X的两个对象,。均2已被创建,则编译程序遇到

如下语句:

obj1=obj2;

就调用默认的赋值运算符函数,将对象。32的数据成员逐域拷贝到

对象objl中。

通常,默认的赋值运算符函数是能够胜任工作的。但在某些特殊情

况下,如类中有指针类型时,使用默认赋值运算符函数会产生错

误,这个错误叫“指针悬挂”,这时程序员不得不自行重载赋值

运算符,请看下面的例子。

1.指针悬挂问题45

例5.11

#include<iostream.h>

#include<string.h>

classstring{

char*ptr;

public:

string(char*s)voidmain()

{ptr=newchar[strk{stringp1("chen");

strcpy(ptr,s);{stringp2("");

}p2=p1;//string类对象间赋值

~string(){deleteptr;cont«"p2:";

voidprint(){cont«|p2.print();

);}

程序运行结果如下:cout«"p1:";

p2:chenp1.print();

pl:□早□早)

NullPointassignment

46

接1例5.11

运行上述程序输出pl时发生错误。原因是执行到赋值语句p2二pl时,

实际是使对象p2的指针ptr与对象pl的指针ptr指向new开辟的同

一个空间。当p2的生命周期(内层的一对花括号间)结束时,系

统自动调用析构函数将这一空间撤消。这样,P2中的ptr原来所

指向的区域被封锁起来,并不能再被使用,这就产生了指针悬挂

问题。这时尽管pl的指针ptr存在,其所指向的空间却无法访问

了,如图所示。当pl的生命走其结束时,将再次调用析构函数,

释放同一空间,从而导致运行错误。

47

动态空间1动态空间2

动态空间2

(b)执行P2二Pl之后(c)p2的生命周期结束后

48

2.显式地定义一个赋值运算符重载函数,解决指针悬挂问题I<

例5.12显式地定义一个赋值运算符重载函:voidmain()

存储区。#include<iostream.h>{stringp1("chen");

#include<string.h>

classstring{{stringp2("");

char*ptr;p2=p1;

public:cout«"p2:";

string(char*)p2.print();

{ptr=newchar[strlen(s)+1];

strcpy(ptr,s);}

)cout«"p1:";

-string(){deleteptr;}p1.print();

voidprint(){count«ptr«endl;})

string&operator=(conststring&);〃声也火队'I且心畀里轨田

);

string&string::opertor=(conststring&s)〃定义赋值运算符重载

{if(this==&s)return*this;程序运行结果如下:

deleteptr;

ptr=newchar[strlen(s.ptr)+1];p2:chen

strcpy(ptr,s.ptr);pl:chen

return*this;

说明:<

⑴赋值运算符“二”只能重载为成员函数,而不能把它重载为友元

函数,因为若把上述赋值运算符“二”重载为友元函数:

friendstring&operator=(string&p2,string&p1)

这时,表达式:p1="chen^^

将被解释为:operator=(p1Jchen")这显然是没有什么问题的,

但对于表达式"chen"=p1

将被解释为:operator=("cherT,p1)

即C++编译器首先将“chen”转换成一个隐藏的string对象,然后

使用对象p2引用该隐藏对象,编译器不认为这个表达式是错误

的,从而将导致赋值语句上的混乱。因此双目赋值运算符应重载

为成员函数的形式,而不能重载为友元函数的形式。

(2)类的赋值运算符“二”可以被重载,但重载了的运算符函数

operator=()不能被继承。

50

537下标运算符“[厂的重载二

C++中运算符“口”可作数组下标,下标运算符的一般使用格式为:

〈基本表达式〉[〈表达式〉]

我们可以对运算符“[厂进行重载,使它起别的作用。

在重载“[『时,C++也把它看成双目运算符,例如X[Y]可看成

[]双目运算符

X左操作数

Y右操作数

设X是某个类的对象,类中定义了重载下标运算符

operator□函数。

则表达式X[5];

可被解释为:X.operator[](5);

对运算符“口”重载定义只能使用成员函数,其形式如下:

返回类型类名::operator[](形参)

(

II函数体

}注意:形参只能有一个。51

重载运算符“[厂来处理数组成员的例子■<

例5.13

#include<iostream.h>

#include<stdlib.h>

classVector4{

private:

intv[4];

public:

Vector4(inta1,inta2,inta3,inta4)

{v[0]=a1;v[1]=a2;v[2]=a3;v[3]=a4;}

);

voidmain()

(

Vector4v(1,2,3,4);

cout«v[2];〃出错,不能访问私有成员。

)

52

即使把intv[4];改为公有成员也出错。要用v.v[2]

我们可用重载运算符“[『的方法,方便的引用数组成员。

例5.14

#include<iostream.h>

#include<stdlib.h>

classVector4{

private:

intv[4];

public:

Vector4(inta1,inta2,inta3,inta4)

{v[0]=a1;v[1]=a2;v[2]=a3;v[3]=a4;}

int&operator[](intbi)

{if(bi<0||bi>=4){

coutvv”数疝下标出界!\n”;

exit(1);

)

returnv[bi];

}

);53

接1例5.14<

voidmain()

(

Vector4v(1,2,3,4);

cout«v[2]«endl;

v[3]=v[2];

cout«v[3]«endl;

v[2]=22;

cout«v[2]«endl;

)

•在上述程序中,并没有创建Vector4的对象数组。程序中的下标

被重载,以便在使用Vector4的对象内的数组成员时,用一种特

殊的方式工作。下标总是返回和下标对应的对象内数组成员的那

个元素

•重载下标运算符”『时,返回一个int的引用,可使重载的“[厂

用的赋值语句的左边,因为在main。中,可对某个数组成员元

素v口赋值。这样,虽然v□是私有的,main()还是能够直接对其

赋值,而不需要使用函数init()54

•在上述程序中,设有对下标的检验,以确保被赋值的数组/L4」

存在,当程序中一旦出现向超出所定义的数组下标范围的数组元素

的赋值,便自动终止程序,以免造成不必要的破坏。

■与函数调用运算符“()”一样,下标运算符“[『不能用友元函数

重载,只能采用成员函数重载。

下面再举一个很有意思的例子:

例如,可以对string数据类型定义以下的”丁运算符重载函数:

classstring{

char*ptr;

II....................

public:

string(constchar*);

char&operator[](int);

II....................

);

55

其中重载“[]”的operator函数定义为:

char&string::opertor[](intI){returnptr[l];}

由于5什M9::(^6「210「[]()的返回值在这里是一个(^2「&,所以函数

可以被用于一个赋值运算符的任一边,例如:

stringop="abed";

op[1]=op[3];

相当与:op.operator[](1)=op.operator1](3);

因而op的薪值为“aded”

56

5.3.8函数调用运算符“()”的重载

C++中运算符“()”可作函数调用,函数调用的一般使用格式为:

〈函数名〉(〈实参表〉)

我们可以对运算符“()”进行重载,使它起别的作用。

在重载“()”时,C++也把它看成双目运算符,例如X(Y)可看成

()双目运算符

X左操作数

Y右操作数

设X是某个类的对象,类中定义了重载下标运算符“()”:

operator()函数。

则表达式X(5);

可被解释为:X.operator()(5);

对运算符“口”重载定义只能使用成员函数,其形式如下:

返回类型类名::operator()(形参表)

(

II函数体

}注意:形参只能有一个。L

例5.15

#include<iostream.h>

classMatrix{

int*m;

introw,col;

public:

Matrix(int,int);

int&operator()(int,int);〃声明重载运算符“()”函

);

Matrix::Matrix(introw,intcol)〃构造函数

{this->row=row;

this->col=col;

m=newint[row*col];

for(inti=0;i<row*col;i++

*(m+i)=i;

)

int&Matrix::operator()(intr,intc)〃定义重载运算符“()”函

(

return(*(m+r*col+c));〃取第r行第c列的元素

}

voidmain()

{

MatrixaM(10,10);〃生成对象aM

cout«aM(3,4);

〃重载运算符“()”的调用,可解释为

aM.perator()(3,4)

aM(3,4)=35;

〃重载运算符“()”的调用,由于引用,可放左边

cout«endl«aM(3,4)«endl;

}N

运行结果:

Q4

再如:对运算符“O”进行重载,使它起别的作甫出一

我们知道,双目运算符是由左边的对象产生对operator函数调用的。

This指针总是指向产生调用的对象。重载运算符“()”的

operator函数可以带任何类型的操作数,返回任何有效的类型。

下面的程序将帮助我们理解函数谡用运算符“()”的重载。

设obj是类myclass的对象,而且类myclass中定义了重载函数调

用运算符“()”,它起的作用是能把对象。bj的数据扩大k倍

和m倍。

对应的运算符函数为myclassoperator()(intk,intm)

则函数调用(把对象。bj的数据扩大10倍和100倍)

obj(10,100);

可被解释为:obj.operator()(10,100);

60

例5.16运算符“()的重载。—

#include<iostream.h>

classmyclass{

inti;

floatj;

public:

myclass(intx=0,floaty=0)

{仁x;j=y;}

myclassoperator()(intk,floatm);

〃声明重载运算符“()”函数

voiddisplay(){00^«1«,5"«j«"\n";}

};

myclassmyclass::operator()(intk,floatm)

〃定义运算符“()”函数

1=1*

j=j*m;〃把myclass的对象中的i放大k倍,j放大m倍。

return*this;

}

61

voidmain()

{myclassobj1(10,11.5);

obj1.display();

obj1(10,100);〃重载运算符“O”的调用,可解

II#^jobj1.perator()(10,.100)

obj1.display();

)

程序运行结果为:

1011.5

1001150.0

说明:函数调用运算符“(

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论