面向对象程序设计多态性_第1页
面向对象程序设计多态性_第2页
面向对象程序设计多态性_第3页
面向对象程序设计多态性_第4页
面向对象程序设计多态性_第5页
已阅读5页,还剩59页未读 继续免费阅读

下载本文档

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

文档简介

面向对象程序设计多态性第1页,共64页,2023年,2月20日,星期四但是,在面向对象程序设计领域,所谓多态性通常特指下述机制:派生类对象可以像基类对象一样使用,同样的消息既可以发送给基类对象也可以发送给派生类对象。第2页,共64页,2023年,2月20日,星期四也就是说,在类等级的不同层次中可以共享一个行为的名字,但是不同层次中的类却各自按自己的需要来实现这个行为。简而言之:在不同的类层次中,同一个消息被不同的对象接收,产生了不同的行为。当一个对象接收到发送给它的消息时,根据该对象所属于的类动态选用在该类中定义的实现算法。第3页,共64页,2023年,2月20日,星期四多态性机制不仅增加了面向对象软件系统的灵活性,进一步减少了冗余信息,而且显著提高了软件的可重用性和可扩充性。本章主要讲述,静态联编与动态联编,虚函数的声明与使用,虚函数应用实例,纯虚函数,多态性带来的好处等内容。第4页,共64页,2023年,2月20日,星期四本章快速索引面向对象程序设计第二版9.1静态联编与动态联编9.2虚函数的声明与使用9.3虚函数应用实例之一9.4纯虚函数9.5虚函数应用实例之二9.6多态性带来的好处9.7小结第5页,共64页,2023年,2月20日,星期四9.1静态联编与动态联编所谓联编(tinding),就是使一个计算机程序的不同部分彼此关联的过程。静态联编在编译阶段完成,因为所有联编过程都在程序开始运行之前完成,因此静态联编也叫先前联编或早期联编。面向对象程序设计第二版第6页,共64页,2023年,2月20日,星期四编译程序在编译时并不确切知道应把发送到对象的消息和实现消息的哪段具体代码联编在一起,而是在运行时才能把函数调用与函数体联系在一起,则称为动态联编。第7页,共64页,2023年,2月20日,星期四9.1.1静态联编 对重载函数的调用是在编译阶段完成联编的,有下述3种区分重载函数的方法:⒈根据实参特征来区分。⒉使用作用域分辨符加以区分。⒊根据对象的类型来区分。

面向对象程序设计第二版第8页,共64页,2023年,2月20日,星期四下面让我们看一个简单程序例子:#include"stdafx.h"#include"iostream.h"classBase{public: voidWho() { cout<<"Iambaseclass\n"; }};第9页,共64页,2023年,2月20日,星期四classFirstDerived:publicBase{public: voidWho() { cout<<"Iamfirstderivedclass\n"; }};第10页,共64页,2023年,2月20日,星期四classSecondDerived:publicBase{public: voidWho() { cout<<"Iamsecondderivedclass\n"; }};第11页,共64页,2023年,2月20日,星期四voidmain(){ Basebase_obj; Base*p;//声明一个指向基类的指针P FirstDerivedfirst_obj; SecondDerivedsecond_obj; p=&base_obj; p->Who(); p=&first_obj; p->Who(); p=&second_obj; p->Who(); first_obj.Who(); second_obj.Who();}运行输出结果:IambaseclassIambaseclassIambaseclassIamfirstderivedclassIamsecondderivedclass第12页,共64页,2023年,2月20日,星期四9.1.2动态联编如果随着指针P实际指向的对象不同,使用语句P->Who();能够调用不同类中Who的相同版本,我们就可以用相同的界面P->Who()访问函数Who的多个实现版本,从而也就能够在程序运行时告诉用户,当时指针P实际指向何类对象。面向对象程序设计第二版第13页,共64页,2023年,2月20日,星期四函数调用P->Who()依赖于程序运行时P的值。虚函数提供的就是这样一种机制。把由指针调用的成员函数声明为虚函数,则声明为指向基类对象的指针,可以根据它在程序运行过程中当时实际指向的对象类型,通过动态联编调用相应类中的虚函数。第14页,共64页,2023年,2月20日,星期四#include<iostream.h>classstudent{public: voidcalct() { cout<<"studentxuefei"<<endl; }};classgrastu:publicstudent{public: voidcalct() { cout<<"grastuxuefei"<<endl; }};第15页,共64页,2023年,2月20日,星期四voidfn(student&x){ x.calct();}voidmain(){ students; grastugs; fn(s); fn(gs);}第16页,共64页,2023年,2月20日,星期四为了把成员函数Who声明为虚函数,我们把上述程序修改为:#include"stdafx.h"#include"iostream.h"classBase{public: virtualvoidWho() //定义Who()为虚函数

{ cout<<"Iambaseclass\n"; }};面向对象程序设计第二版第17页,共64页,2023年,2月20日,星期四classFirstDerived:publicBase{public: voidWho() { cout<<"Iamfirstderivedclass\n"; }};第18页,共64页,2023年,2月20日,星期四classSecondDerived:publicBase{public: voidWho() { cout<<"Iamsecondderivedclass\n";

}};面向对象程序设计第二版第19页,共64页,2023年,2月20日,星期四voidmain(){ Basebase_obj; Base*p;

FirstDerivedfirst_obj; SecondDerivedsecond_obj; p=&base_obj; p->Who(); p=&first_obj; p->Who(); p=&second_obj; p->Who();}第20页,共64页,2023年,2月20日,星期四运行结果如下:IambaseclassIamfirstderivedclassIamsecondderivedclass第21页,共64页,2023年,2月20日,星期四9.2虚函数的声明与使用

9.2.1声明虚函数声明虚函数的一般格式如下:

virtual函数原型;⑴必须首先在基类中声明虚函数。⑵派生类中与基类虚函数原型完全相同的成员函数,即使在说明时前面没有冠以关键字virtual也自动成为虚函数。面向对象程序设计第二版第22页,共64页,2023年,2月20日,星期四9.2.1声明虚函数⑶只有非静态成员函数可以声明为虚函数。⑷不允许在派生类中定义与基类虚函数名字及参数特征都相同,仅仅返回类型不同的成员函数。编译时出错。⑸系统把函数名相同但参数特征不同的函数视为不同的函数。⑹通过声明虚函数来使用C++提供的多态性机制时,派生类应该从它的基类公有派生。面向对象程序设计第二版第23页,共64页,2023年,2月20日,星期四#include<iostream.h>classstudent{public:virtualvoidcalct(intx) { cout<<"studentxuefei"<<x<<endl; }};classgrastu:publicstudent{public: virtualvoidcalct(floatx) { cout<<"grastuxuefei"<<x<<endl;

}};第24页,共64页,2023年,2月20日,星期四voidfn(student&x){ inti=1; floatj=9.8; x.calct(i); x.calct(j);}voidmain(){ students; grastugs; fn(s); fn(gs);}第25页,共64页,2023年,2月20日,星期四9.2.2使用虚函数仅当程序中用指向基类对象的指针变量或引用基类对象的引用变量调用虚函数时,系统才以动态联编方式实现对虚函数的调用,例如:面向对象程序设计第二版第26页,共64页,2023年,2月20日,星期四#include“stdafx.h”#include“iostream.h”classA{public://voidshow() virtualvoidshow()//必须要有virtual关键字,才能实现多态性

{ cout<<"AAA..."<<endl; }};第27页,共64页,2023年,2月20日,星期四classB:publicA{public: voidshow() //派生类中原型相同的函数可以不加关键字, //也被默认为虚函数

{ cout<<"BBB..."<<endl; }};面向对象程序设计第二版第28页,共64页,2023年,2月20日,星期四classC:publicB{public: voidshow() //派生类中原型相同的函数可以不加关键字, //也被默认为虚函数

{ cout<<"CCC..."<<endl; }};第29页,共64页,2023年,2月20日,星期四//voiddisplay(Aa)//这个函数不能实现多态性voiddisplay(A&a)//说明一个实现多态性的函数,参数类型必须是引用或者指针类型{ a.show();}面向对象程序设计第二版第30页,共64页,2023年,2月20日,星期四voidmain(){ Aa; Bb; Cc; display(a); display(b);//通过实参、形参结合的形式,将派生类对象b传递给基类对象

display(c);//通过实参、形参结合的形式,将派生类对象c传递给基类对象

/*以上部分是通过实参、形参结合的形式,下面我们介绍通过赋值语句的形式*/第31页,共64页,2023年,2月20日,星期四

A*a_p=&a; B*b_p=&b; C*c_p=&c; a_p->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页,2023年,2月20日,星期四运行结果:AAA… BBB… CCC…AAA… BBB… CCC…第33页,共64页,2023年,2月20日,星期四关于程序的几点说明:⒈准备实现多态性的方法前面必须加关键字virtual,这被称为虚函数,是实现多态性的必要条件。⒉使用多态性时,它的调用接口形式是不变的,关键是看基类对象的指针指向了哪个对象,就调用了哪个对象的方法。而这种指向有两种表现形式:一种是通过实参、形参结合的形式;另一种是通过赋值语句的形式。面向对象程序设计第二版第34页,共64页,2023年,2月20日,星期四⒊为了实现动态联编,函数中参数类型必须是引用或者指针类型(如display(A*a);或display(A&a);)。通常用指向第一次定义虚函数的基类对象的指针来调用虚函数,可以获得运行时的多态性。(如例中使用“a_p->show();”调用,而没有使用其他派生类对象指针)第35页,共64页,2023年,2月20日,星期四⒋使用普通对象调用虚函数时,系统仍然以静态联编方式完成对虚函数的调用(如例中display(Aa);),请读者上机实践。⒌基类与派生类是相对的,因此,并非在任何情况下都必须首先在类等级的最高层类内声明虚函数。第36页,共64页,2023年,2月20日,星期四9.2.3动态联编的实现C++语言中的动态联编是通过使用虚函数表(VirtualFunctionTable)来实现的,虚函数表也称为v-表。每个类的实例都有一个隐含的指向该类v-表的指针,当执行诸如a_p->show()这样的语句时,系统首先取a_p所实际指向的对象中的v-表指针,然后调用由这个v-表中指针项所指定的函数show(),从而实现了对不同类的虚函数的调用。面向对象程序设计第二版第37页,共64页,2023年,2月20日,星期四9.3虚函数应用实例之一下面是实现上述要求的程序:#include"stdafx.h"#include"iostream.h"classFigure{protected: floatx,y;public: voidSet(floati,floatj=0) { x=i;y=j; } virtualvoidShowArea(){}};面向对象程序设计第二版第38页,共64页,2023年,2月20日,星期四classTriangle:publicFigure{public: voidShowArea() { cout<<"Trianglewithheight"<<x<<"andbase"; cout<<y<<"hasanareaof"<<x*0.5*y<<'\n'; }};第39页,共64页,2023年,2月20日,星期四classSquare:publicFigure{public: voidShowArea() { cout<<"Squarewithdimension"<<x<<'*'<<y; cout<<"hasanareaof"<<x*y<<'\n'; }};面向对象程序设计第二版9.3虚函数应用实例之一

第40页,共64页,2023年,2月20日,星期四classCircle:publicFigure{public: voidShowArea() { cout<<"Circlewithradius"<<x; cout<<"hasanareaof"<<3.14*x*x<<'\n';

}};第41页,共64页,2023年,2月20日,星期四voidmain(){ Figure*p[3]; Trianglet;

Squares; Circlec; p[0]=&t; p[0]->Set(12.0,8.0); p[1]=&s; p[1]->Set(12.0,8.0); p[2]=&c; p[2]->Set(10.0); for(inti=0;i<3;i++) p[i]->ShowArea();}面向对象程序设计第二版第42页,共64页,2023年,2月20日,星期四运行上列程序,得到下列输出结果:Trianglewithheight12andbase8hasanareaof48Squarewithdimension12*8hasanareaof96Circlewithradius10hasanareaof314第43页,共64页,2023年,2月20日,星期四9.4纯虚函数纯虚函数是在基类中声明的虚函数,它在声明它的基类中没有定义,要求任何派生类都必须为该虚函数定义自己的版本。说明纯虚函数的一般格式如下:

virtual函数原型=0;例如,为了把Figure类的虚函数ShowArea说明为纯虚函数,应该像下面那样声明它:

virtualvoidShowArea()=0;面向对象程序设计第二版第44页,共64页,2023年,2月20日,星期四9.4纯虚函数关于纯虚函数和抽象类的使用,C++语言有以下规定:⑴抽象类只能作为其他类的基类,不能声明抽象类的实例。⑵在从抽象类派生出的新类中,必须重新定义其父类的每个纯虚函数;或者把这些函数继续声明为纯虚函数,这样做派生类也就成为抽象类。面向对象程序设计第二版第45页,共64页,2023年,2月20日,星期四9.4纯虚函数⑶在类等级的上层定义一个或几个抽象类作为基类,而在下层定义由基类派生出的具体类的情况比较常见,但是,不允许从具体类派生出抽象类。所谓具体类,就是不包含纯虚函数的普通类。⑷在抽象类中也可以定义普通成员函数或虚函数,虽然不能为抽象类声明对象,但仍然可以通过派生类对象来调用这些不是纯虚函数的函数。面向对象程序设计第二版第46页,共64页,2023年,2月20日,星期四9.5虚函数应用实例之二要求设计一个面向对象的程序,以完成下列几项功能:⒈在显示器荧光屏上指定位置显示指定的图形(点或圆),或擦去屏幕上正在显示的某个图形; ⒉放大或缩小屏幕上正在显示的圆;⒊把某个图形从屏幕上原来的位置移动到指定的新位置;⒋在屏幕上沿指定方向以指定的速度连续拖动指定的图形。(参考教材请同学上机练习)面向对象程序设计第二版第47页,共64页,2023年,2月20日,星期四#include”graphics.h”#include”conio.h”enumBoolean{false,true};classLocation{protected: intX; intY;public: Location(intInitX,intInitY) { X=InitX; Y=InitY; } intGetX(){returnX;} intGetY(){returnY;}};面向对象程序设计第二版第48页,共64页,2023年,2月20日,星期四classPoint:publicLocation{ protected: BooleanVisible;public: Point(intInitX,intInitY); virtualvoidShow(); //虚函数

virtualvoidHide(); //虚函数

virtualvoidDrag(intDragBy); //虚函数

BooleanIsVisible(){returnVisible;}voidMoveTo(intNewX,intNewY);};面向对象程序设计第二版第49页,共64页,2023年,2月20日,星期四classCircle:publicPoint{protected: intRadius;public: Circle(intInitX,intInitY,intInitRadius); voidShow(); //虚函数

voidHide(); //虚函数

voidExpand(intExpandBy); voidContract(intContractBy);};第50页,共64页,2023年,2月20日,星期四BooleanGetDelta(int&DeltaX,int&DeltaY){ charKeyChar; BooleanQuit; DeltaX=0; DeltaY=0;

面向对象程序设计第二版第51页,共64页,2023年,2月20日,星期四do { KeyChar=getch(); //读键盘输入

if(KeyChar==13)return(false); //回车键结束拖动

if(KeyChar==0) //扩展键码

{ Quit=true; //假设按对了键盘

KeyChar=getch(); //读剩余的键码

switch(KeyChar) { case72:DeltaY=-1;break; //上箭头键

case80:DeltaY=1;break; //下箭头键

case75:DeltaX=-1;break; //左箭头键

case77:DeltaX=1;break; //右箭头键

default:Quit=false; //错键

} } }while(!Quit); //按错了键可改敲

return(true); //箭头键继续拖动}第52页,共64页,2023年,2月20日,星期四Point∷Point(intInitX,intInitY):Location(InitX,InitY){ Visible=false; //缺省为不可见状态}voidPoint∷Show(){ Visible=true; //可见状态

putpixel(X,Y,getcolor()); //使用缺省颜色显示}面向对象程序设计第二版第53页,共64页,2023年,2月20日,星期四voidPoint∷Hide(){ Visible=false; //不可见状态

putpixel(X,Y,getbkcolor()); //用背景色以擦去这个点}voidPoint∷MoveTo(intNewX,intNewY){ Hide(); //使该点成为不可见

X=NewX; //修改X,Y坐标到新位置

Y=NewY; Show(); //在新位置显示该点}第54页,共64页,2023年,2月20日,星期四voidPoint::Drag(intDragBy) //参数为拖动的步长{ intDeltaX,DeltaY; intFigureX,FigureY; Show(); //显示欲拖动的图形

FigureX=X; //图形初始位置

FigureY=Y; while(GetDelta(DeltaX,DeltaY)) //下面是完成拖动操作的循环语句

{ FigureX+=(DeltaX*DragBy); //修改坐标值

FigureY+=(DeltaY*DragBy); MoveTo(FigureX,FigureY); }}第55页,共64页,2023年,2月20日,星期四Circle::Circle(intInitX,intInitY,intInitRadius):Point(InitX,InitY){ Radius=InitRadius;}voidCircle::Show(){ Visible=true; circle(X,Y,Radius); //画圆}第56页,共64页,2023年,2月20日,星期四voidCircle::Hide(){ intTempColor; TempColor=getcolor(); //保存当前前景色

setcolor(getbkcolor()); //令背景色为画圆的颜色

Visible=false; circle(X,Y,Radius); //用背景色画圆

setcolor(TempColor); //恢复当前前景色}面向对象程序设计第二版第57页,共64页,2023年,2月20日,星期四voidCircle::Expand(intExpandBy){ Hide(); //擦去旧圆

Radius+=ExpandBy; //扩大半径

if(Radius<0) //避免半径值为负数Radius=0; Show(); //显示新圆}voidCircle::Contract(intContractBy){ Expand(-ContractBy); //用Radius-ContractBy为半径画圆}第58页,共64页,2023年,2月20日,星期四voidmain(){ intgraphdriver=DETECT,graphmode; initgraph(&graphdriver,&graphmode,”..\\bgi”); CircleMyCircle(100,200,50); //说明一个Circle对象

温馨提示

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

评论

0/150

提交评论