面向对象程序设计多态性优秀课件.ppt
面向对象程序设计多态性第1页,本讲稿共64页n但是,在面向对象程序设计领域,所谓多态性通常特指下述机制:派生类对象可以像基类对象一样使用,同样的消息既可以发送给基类对象也可以发送给派生类对象。第2页,本讲稿共64页n也就是说,在类等级的不同层次中可以共享一个行为的名字,但是不同层次中的类却各自按自己的需要来实现这个行为。n简而言之:在不同的类层次中,同一个消息被不同的对象接收,产生了不同的行为。当一个对象接收到发送给它的消息时,根据该对象所属于的类动态选用在该类中定义的实现算法。第3页,本讲稿共64页n多态性机制不仅增加了面向对象软件系统的灵活性,进一步减少了冗余信息,而且显著提高了软件的可重用性和可扩充性。n本章主要讲述,静态联编与动态联编,虚函数的声明与使用,虚函数应用实例,纯虚函数,多态性带来的好处等内容。第4页,本讲稿共64页本章快速索引面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版9.1 9.1 静态联编与动态联编静态联编与动态联编 9.2 9.2 虚函数的声明与使用虚函数的声明与使用9.3 9.3 虚函数应用实例之一虚函数应用实例之一 9.4 9.4 纯虚函数纯虚函数9.5 9.5 虚函数应用实例之二虚函数应用实例之二 9.6 9.6 多态性带来的好处多态性带来的好处 9.7 9.7 小结小结第5页,本讲稿共64页9.1 静态联编与动态联编 n所谓联编(tinding),就是使一个计算机程序的不同部分彼此关联的过程。n静态联编在编译阶段完成,因为所有联编过程都在程序开始运行之前完成,因此静态联编也叫先前联编或早期联编。面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第6页,本讲稿共64页n编译程序在编译时并不确切知道应把发送到对象的消息和实现消息的哪段具体代码联编在一起,而是在运行时才能把函数调用与函数体联系在一起,则称为动态联编。第7页,本讲稿共64页9.1.1静态联编对重载函数的调用是在编译阶段完成联编的,有下述3种区分重载函数的方法:根据实参特征来区分。使用作用域分辨符加以区分。根据对象的类型来区分。面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第8页,本讲稿共64页下面让我们看一个简单程序例子:#include stdafx.h#include iostream.hclass Basepublic:void Who()cout I am base classn;第9页,本讲稿共64页class FirstDerived:public Basepublic:void Who()cout I am first derived classn;第10页,本讲稿共64页class SecondDerived:public Basepublic:void Who()cout Who();p=&first_obj;p-Who();p=&second_obj;p-Who();first_obj.Who();second_obj.Who();运行输出结果:I am base classI am base classI am base classI am first derived classI am second derived class第12页,本讲稿共64页9.1.2动态联编 n如果随着指针P实际指向的对象不同,使用语句P-Who();能够调用不同类中Who的相同版本,我们就可以用相同的界面P-Who()访问函数Who的多个实现版本,从而也就能够在程序运行时告诉用户,当时指针P实际指向何类对象。面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第13页,本讲稿共64页n函数调用P-Who()依赖于程序运行时P的值。虚函数提供的就是这样一种机制。把由指针调用的成员函数声明为虚函数,则声明为指向基类对象的指针,可以根据它在程序运行过程中当时实际指向的对象类型,通过动态联编调用相应类中的虚函数。第14页,本讲稿共64页n#include nclass studentnnpublic:nvoid calct()nncoutstudent xuefeiendl;nn;nclass grastu:public studentnnpublic:nvoid calct()nncoutgrastu xuefeiendl;nn;第15页,本讲稿共64页nvoid fn(student&x)nnx.calct();nnvoid main()nnstudent s;ngrastu gs;nfn(s);nfn(gs);n第16页,本讲稿共64页为了把成员函数Who声明为虚函数,我们把上述程序修改为:#include stdafx.h#include iostream.hclass Basepublic:virtual void Who()/定义Who()为虚函数 cout I am base classn;面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第17页,本讲稿共64页class FirstDerived:public Basepublic:void Who()cout I am first derived classn;第18页,本讲稿共64页class SecondDerived:public Basepublic:void Who()cout Who();p=&first_obj;p-Who();p=&second_obj;p-Who();第20页,本讲稿共64页n运行结果如下:nI am base classnI am first derived classnI am second derived class第21页,本讲稿共64页9.2 虚函数的声明与使用 9.2.1声明虚函数声明虚函数的一般格式如下:virtual 函数原型;必须首先在基类中声明虚函数。派生类中与基类虚函数原型完全相同的成员函数,即使在说明时前面没有冠以关键字virtual也自动成为虚函数。面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第22页,本讲稿共64页9.2.1声明虚函数 只有非静态成员函数可以声明为虚函数。不允许在派生类中定义与基类虚函数名字及参数特征都相同,仅仅返回类型不同的成员函数。编译时出错。系统把函数名相同但参数特征不同的函数视为不同的函数。通过声明虚函数来使用C+提供的多态性机制时,派生类应该从它的基类公有派生。面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第23页,本讲稿共64页n#include nclass studentnnpublic:n virtual void calct(int x)nncoutstudent xuefei xendl;nn;nclass grastu:public studentnnpublic:nvirtual void calct(float x)nncoutgrastu xuefei xendl;nnn;第24页,本讲稿共64页nvoid fn(student&x)nnint i=1;nfloat j=9.8;nx.calct(i);nx.calct(j);nnvoid main()nnstudent s;ngrastu gs;nfn(s);nfn(gs);n第25页,本讲稿共64页9.2.2使用虚函数n仅当程序中用指向基类对象的指针变量或引用基类对象的引用变量调用虚函数时,系统才以动态联编方式实现对虚函数的调用,例如:面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第26页,本讲稿共64页#include“stdafx.h”#include“iostream.h”class Apublic:/void show()virtual void show()/必须要有virtual关键字,才能实现多态性cout AAA.endl;第27页,本讲稿共64页class B:public Apublic:void show()/派生类中原型相同的函数可以不加关键字,/也被默认为虚函数cout BBB.endl;面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第28页,本讲稿共64页class C:public Bpublic:void show()/派生类中原型相同的函数可以不加关键字,/也被默认为虚函数cout CCC.show();a_p=b_p;/通过赋值形式,将派生类对象b的指针传递给基类对象指针a_p-show();/调用的接口形式不变,仍为“a_p-show()”,但调用的是b中的show()a_p=c_p;/通过赋值形式,将派生类对象c的指针传递给基类对象指针a_p-show();/调用的接口形式不变,仍为“a_p-show()”,但调用的是c中的show()面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第32页,本讲稿共64页n运行结果:nAAA nBBB nCCCnAAA nBBB nCCC第33页,本讲稿共64页关于程序的几点说明:准备实现多态性的方法前面必须加关键字virtual,这被称为虚函数,是实现多态性的必要条件。使用多态性时,它的调用接口形式是不变的,关键是看基类对象的指针指向了哪个对象,就调用了哪个对象的方法。而这种指向有两种表现形式:一种是通过实参、形参结合的形式;另一种是通过赋值语句的形式。面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第34页,本讲稿共64页为了实现动态联编,函数中参数类型必须是引用或者指针类型(如display(A*a);或display(A&a);)。通常用指向第一次定义虚函数的基类对象的指针来调用虚函数,可以获得运行时的多态性。(如例中使用“a_p-show();”调用,而没有使用其他派生类对象指针)第35页,本讲稿共64页使用普通对象调用虚函数时,系统仍然以静态联编方式完成对虚函数的调用(如例中display(A a);),请读者上机实践。基类与派生类是相对的,因此,并非在任何情况下都必须首先在类等级的最高层类内声明虚函数。第36页,本讲稿共64页9.2.3 动态联编的实现nC+语言中的动态联编是通过使用虚函数表(Virtual Function Table)来实现的,虚函数表也称为v-表。n每个类的实例都有一个隐含的指向该类v-表的指针,当执行诸如a_p-show()这样的语句时,系统首先取a_p所实际指向的对象中的v-表指针,然后调用由这个v-表中指针项所指定的函数show(),从而实现了对不同类的虚函数的调用。面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第37页,本讲稿共64页9.3 虚函数应用实例之一 下面是实现上述要求的程序:#include stdafx.h#include iostream.hclass Figureprotected:float x,y;public:void Set(float i,float j=0)x=i;y=j;virtual void ShowArea();面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第38页,本讲稿共64页class Triangle:public Figure public:void ShowArea()cout Triangle with height x and base;cout y has an area of x*0.5*y n;第39页,本讲稿共64页class Square:public Figurepublic:void ShowArea()cout Square with dimension x *y;cout has an area of x*y n;面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版9.3 虚函数应用实例之一 第40页,本讲稿共64页class Circle:public Figurepublic:void ShowArea()cout Circle with radius x;cout has an area of 3.14*x*x Set(12.0,8.0);p 1=&s;p 1-Set(12.0,8.0);p 2=&c;p 2-Set(10.0);for(int i=0;i ShowArea();面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第42页,本讲稿共64页n运行上列程序,得到下列输出结果:nTriangle with height 12 and base 8 has an area of 48nSquare with dimension 12*8 has an area of 96nCircle with radius 10 has an area of 314第43页,本讲稿共64页9.4 纯虚函数 n纯虚函数是在基类中声明的虚函数,它在声明它的基类中没有定义,要求任何派生类都必须为该虚函数定义自己的版本。n说明纯虚函数的一般格式如下:nvirtual 函数原型=0;n例如,为了把Figure类的虚函数ShowArea说明为纯虚函数,应该像下面那样声明它:nvirtual void ShowArea()=0;面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第44页,本讲稿共64页9.4 纯虚函数 n关于纯虚函数和抽象类的使用,C+语言n有以下规定:n抽象类只能作为其他类的基类,不能声明抽象类的实例。n 在从抽象类派生出的新类中,必须重新定义其父类的每个纯虚函数;或者把这些函数继续声明为纯虚函数,这样做派生类也就成为抽象类。面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第45页,本讲稿共64页9.4 纯虚函数 在类等级的上层定义一个或几个抽象类作为基类,而在下层定义由基类派生出的具体类的情况比较常见,但是,不允许从具体类派生出抽象类。所谓具体类,就是不包含纯虚函数的普通类。在抽象类中也可以定义普通成员函数或虚函数,虽然不能为抽象类声明对象,但仍然可以通过派生类对象来调用这些不是纯虚函数的函数。面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第46页,本讲稿共64页9.5 虚函数应用实例之二要求设计一个面向对象的程序,以完成下列几项功能:在显示器荧光屏上指定位置显示指定的图形(点或圆),或擦去屏幕上正在显示的某个图形;放大或缩小屏幕上正在显示的圆;把某个图形从屏幕上原来的位置移动到指定的新位置;在屏幕上沿指定方向以指定的速度连续拖动指定的图形。(参考教材请同学上机练习)面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第47页,本讲稿共64页#include”graphics.h”#include”conio.h”enum Booleanfalse,true;class Locationprotected:int X;int Y;public:Location(int InitX,int InitY)X=InitX;Y=InitY;int GetX()return X;int GetY()return Y;面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第48页,本讲稿共64页class Point:public Location protected:Boolean Visible;public:Point(int InitX,int InitY);virtual void Show();/虚函数virtual void Hide();/虚函数virtual void Drag(int DragBy);/虚函数Boolean IsVisible()return Visible;void MoveTo(int NewX,int NewY);面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第49页,本讲稿共64页class Circle:public Pointprotected:int Radius;public:Circle(int InitX,int InitY,int InitRadius);void Show();/虚函数void Hide();/虚函数void Expand(int ExpandBy);void Contract(int ContractBy);第50页,本讲稿共64页Boolean GetDelta(int&DeltaX,int&DeltaY)char KeyChar;Boolean Quit;DeltaX=0;DeltaY=0;面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第51页,本讲稿共64页doKeyChar=getch();/读键盘输入if(KeyChar=13)return(false);/回车键结束拖动if(KeyChar=0)/扩展键码Quit=true;/假设按对了键盘KeyChar=getch();/读剩余的键码switch(KeyChar)case 72:DeltaY=-1;break;/上箭头键case 80:DeltaY=1;break;/下箭头键case 75:DeltaX=-1;break;/左箭头键case 77:DeltaX=1;break;/右箭头键default:Quit=false;/错键while(!Quit);/按错了键可改敲return(true);/箭头键继续拖动 第52页,本讲稿共64页Point Point(int InitX,int InitY):Location(InitX,InitY)Visible=false;/缺省为不可见状态void Point Show()Visible=true;/可见状态putpixel(X,Y,getcolor();/使用缺省颜色显示面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第53页,本讲稿共64页void Point Hide()Visible=false;/不可见状态putpixel(X,Y,getbkcolor();/用背景色以擦去这个点void Point MoveTo(int NewX,int NewY)Hide();/使该点成为不可见X=NewX;/修改X,Y坐标到新位置Y=NewY;Show();/在新位置显示该点 第54页,本讲稿共64页void Point:Drag(int DragBy)/参数为拖动的步长int DeltaX,DeltaY;int FigureX,FigureY;Show();/显示欲拖动的图形FigureX=X;/图形初始位置FigureY=Y;while(GetDelta(DeltaX,DeltaY)/下面是完成拖动操作的循环语句FigureX+=(DeltaX*DragBy);/修改坐标值FigureY+=(DeltaY*DragBy);MoveTo(FigureX,FigureY);第55页,本讲稿共64页Circle:Circle(int InitX,int InitY,int InitRadius):Point(InitX,InitY)Radius=InitRadius;void Circle:Show()Visible=true;circle(X,Y,Radius);/画圆 第56页,本讲稿共64页void Circle:Hide()int TempColor;TempColor=getcolor();/保存当前前景色setcolor(getbkcolor();/令背景色为画圆的颜色Visible=false;circle(X,Y,Radius);/用背景色画圆setcolor(TempColor);/恢复当前前景色面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第57页,本讲稿共64页void Circle:Expand(int ExpandBy)Hide();/擦去旧圆Radius+=ExpandBy;/扩大半径if(Radius 0)/避免半径值为负数Radius=0;Show();/显示新圆void Circle:Contract(int ContractBy)Expand(-ContractBy);/用Radius-ContractBy为半径画圆 第58页,本讲稿共64页void main()int graphdriver=DETECT,graphmode;initgraph(&graphdriver,&graphmode,”.bgi”);Circle MyCircle(100,200,50);/说明一个Circle对象MyCircle.Show();/显示它getch();/等待按下任意键MyCircle.MoveTo(200,250);/移动圆getch();MyCircle.Expand(50);/放大圆getch();MyCircle.Contract(75);/缩小圆getch();MyCircle.Drag(5);/拖动圆closegraph();面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第59页,本讲稿共64页9.6多态性带来的好处多态性主要带来以下几点好处:在类等级的不同层次中,完成相同功能的成员函数使用相同名字,在程序运行时根据调用该函数的指针实际指向的对象的类型,动态地决定使用该函数的哪个版本。进一步减少了信息冗余。显著提高了程序的可重用性和可扩充性,因而进一步提高了程序的可维护性。面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第60页,本讲稿共64页9.6多态性带来的好处 例如,扩充上节的程序,使它增加移动和拖动弧形对象的功能。为此,仅需像下面那样定义新的派生类Arc,对原有程序一点也不需改动:面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第61页,本讲稿共64页class Arc:public Circleint StartAngle;int EndAngle;public:Arc(int InitX,int InitY,int InitRadius,int InitStartAngle,int InitEnd-Angle):Circle(InitX,InitY,InitRadius)StartAngle=InitStartAngle;EndAngle=InitEndAngle;void Show();/虚函数void Hide();/虚函数;第62页,本讲稿共64页void Arc:Show()Visible=true;arc(X,Y,StartAngle,EndAngle,Radius);void Arc:Hide()int TempColor;TempColor=getcolor();setcolor(getbkcolor();Visible=false;arc(X,Y,StartAngle,EndAngle,Radius);setcolor(TempColor);面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第63页,本讲稿共64页面向对象程序设计面向对象程序设计面向对象程序设计面向对象程序设计 第二版第二版第二版第二版第九章 多态性完完第64页,本讲稿共64页