《类的多态性优秀课件.ppt》由会员分享,可在线阅读,更多相关《类的多态性优秀课件.ppt(24页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、类的多态性第1页,本讲稿共24页第4章 类的多态性第2页,本讲稿共24页一、虚函数一、虚函数1、概述、概述通俗地讲,多态是指具有相似功能的不同函数使用同一个名称来表通俗地讲,多态是指具有相似功能的不同函数使用同一个名称来表示,从而可以使用相同的调用方式调用具有不同功能的同名函数。示,从而可以使用相同的调用方式调用具有不同功能的同名函数。抽象地讲,多态是指同样的消息被不同类型的对象接收时导致完全不同抽象地讲,多态是指同样的消息被不同类型的对象接收时导致完全不同的行为。的行为。分类:分类:(1)重载多态:普通函数、类的成员函数的重载重载多态:普通函数、类的成员函数的重载(2)包含多态:基类和派生类
2、中同名成员函数问题,虚函数包含多态:基类和派生类中同名成员函数问题,虚函数(3)参数多态:函数和类的模板,通过不同实际参数的类型实例化,得到不同参数多态:函数和类的模板,通过不同实际参数的类型实例化,得到不同数据类型的相同操作。数据类型的相同操作。第3页,本讲稿共24页成员函数重载:成员函数重载:在子类中可以重新定义成员函数。如果增加一个与基类在子类中可以重新定义成员函数。如果增加一个与基类成员函数同名的成员函数,但函数的形式参数个数、类型、次成员函数同名的成员函数,但函数的形式参数个数、类型、次序不同,则编译程序可以完全识别确定。序不同,则编译程序可以完全识别确定。虚函数:虚函数:如子类中定
3、义的成员函数的原型与父类中的函数完全如子类中定义的成员函数的原型与父类中的函数完全相同,采用同名覆盖或者虚函数来解决,用虚函数时系统相同,采用同名覆盖或者虚函数来解决,用虚函数时系统编译不能完全确定。编译不能完全确定。第4页,本讲稿共24页 联编:联编:指一个计算机程序自身彼此关联的过程。可以理解成比编译指一个计算机程序自身彼此关联的过程。可以理解成比编译中连接更广泛的一种连接形式。中连接更广泛的一种连接形式。静态联编(先期联编):静态联编(先期联编):工作出现在编译阶段,在程序开始运行之前完成。即:在编译时就工作出现在编译阶段,在程序开始运行之前完成。即:在编译时就解决了程序中的操作调用与执
4、行该操作代码间的关系。解决了程序中的操作调用与执行该操作代码间的关系。动态联编(迟后联编):动态联编(迟后联编):在编译阶段不能确定将要调用的函数,只有在程序执行在编译阶段不能确定将要调用的函数,只有在程序执行时才能确定将要调用的函数。即,联编工作在程序运行时进时才能确定将要调用的函数。即,联编工作在程序运行时进行。行。第5页,本讲稿共24页class stu public:float cal();class graduatestu:public stupublic:float cal();例、基类例、基类/派生类中有相同的成员函数派生类中有相同的成员函数void main()stu s;gr
5、aduatestu gs;s.cal();gs.cal();注:注:编译时可确定编译时可确定-静态联编静态联编第6页,本讲稿共24页class stu public:float cal();class graduatestu:public stupublic:float cal();void f(stu&x)x.cal();void main()stu s;graduatestu gs;f(s);f(gs);思考:思考:能不能使函数能不能使函数f根据传进来的对象的类型来决定到底是调用基类的成员函数还根据传进来的对象的类型来决定到底是调用基类的成员函数还是派生类的成员函数是派生类的成员函数第7页
6、,本讲稿共24页2、虚函数、虚函数在一个类中用保留字在一个类中用保留字virtual定义的成员函数,称为虚函数。定义的成员函数,称为虚函数。class basepublic:virtual void f()coutIn base.n;class subcla:public basepublic:virtual void f()coutIn suncla.n;class subclb:public basepublic:virtual void f()coutIn sunclb.n;void test(base&b)b.f();/动态决定哪一个成员函数动态决定哪一个成员函数void main()
7、base bc;subcla sc;subclb sb;test(bc);test(sc);test(sb);第8页,本讲稿共24页3,虚函数的使用,虚函数的使用class memberpublic:virtual void answer()coutI am a member.n;class teacher:public memberpublic:virtual void answer()coutI am a teacher.n;class student:public memberpublic:virtual void answer()coutanswer();who=&te;who-ans
8、wer();who=&st;who-answer();相同的形式产生不同的动作相同的形式产生不同的动作第9页,本讲稿共24页运行结果:I am a member.I am a teacher.I am a student.练习练习4.1 已知有图形圆和矩形已知有图形圆和矩形,圆有位置信息圆心点、填充圆有位置信息圆心点、填充颜色、半径等属性,有计算面积和周长的方法;矩形有位颜色、半径等属性,有计算面积和周长的方法;矩形有位置信息左上角的点、填充颜色、长和宽等属性,有计算面置信息左上角的点、填充颜色、长和宽等属性,有计算面积和周长的方法。要求创建一个图形的基类并采用虚函数积和周长的方法。要求创建一
9、个图形的基类并采用虚函数的方式来实现求面积、周长以及显示图形具体信息的功能。的方式来实现求面积、周长以及显示图形具体信息的功能。第10页,本讲稿共24页4、注意事项、注意事项(1)虚函数是成员函数,也只有成员函数才能说明成虚函数虚函数是成员函数,也只有成员函数才能说明成虚函数(2)内联函数不能是虚函数,因为内联函数是不能在运行中动态确定内联函数不能是虚函数,因为内联函数是不能在运行中动态确定其位置其位置(3)构造函数不能是虚函数,但可以调用虚函数构造函数不能是虚函数,但可以调用虚函数(4)析构函数可以是虚函数:析构函数可以是虚函数:声明语法:声明语法:virtual 类名类名()例如:例如:c
10、lass Bpublic:virtual B();一旦基类的析构函数被声明为虚函数,所有派生类的析构函数自动变一旦基类的析构函数被声明为虚函数,所有派生类的析构函数自动变成虚函数成虚函数第11页,本讲稿共24页(5)虚函数与成员函数的重载不同;虚函数与成员函数的重载不同;虚函数:要求函数原型相同;否则,即使注明虚函数:要求函数原型相同;否则,即使注明virtual 也不能动态联编。也不能动态联编。函数重载:函数名相同,其他不同;函数重载:函数名相同,其他不同;(6)基类的虚函数在派生类中仍然是虚函数;在派生类中重定义继基类的虚函数在派生类中仍然是虚函数;在派生类中重定义继承成员虚函数,即使不用
11、承成员虚函数,即使不用virtual,也是虚函数。也是虚函数。(7)动态联编只能用指针、引用实现。否则,变成静态联编。动态联编只能用指针、引用实现。否则,变成静态联编。例,例,void test(base&b)b.f();不能写:不能写:void test(base b)b.f();第12页,本讲稿共24页5、虚函数与构造、虚函数与构造/析构函数析构函数构造函数中调用虚函数时,采用静态联编构造函数中调用虚函数时,采用静态联编。即构造函数调用。即构造函数调用的虚函数是自己类中实现的虚函数。如自己类中没有实现这的虚函数是自己类中实现的虚函数。如自己类中没有实现这个虚函数,则调用基类中的虚函数,而不
12、是任何派生类中的个虚函数,则调用基类中的虚函数,而不是任何派生类中的虚函数。虚函数。析构函数中调用虚函数的规则一样。析构函数中调用虚函数的规则一样。第13页,本讲稿共24页例,构造函数中用虚函数例,构造函数中用虚函数class Apublic:A()virtual void f()coutA:f()called.n;class B:public Apublic:B()f();void g()f();/可以显式调用,如可以显式调用,如 B:f()virtual void f()coutB:f()called.n;class C:public Bpublic:C()virtual void f()
13、coutC:f()called.n;void main()C c;c.g();B:f()called.C:f()called.第14页,本讲稿共24页二、纯虚函数和抽象类二、纯虚函数和抽象类 纯虚函数纯虚函数是一种特殊的虚函数,它的一般格式:是一种特殊的虚函数,它的一般格式:class virtual ()=0;在基类中声明但没有定义的虚函数。任何它的派生类都必须重在基类中声明但没有定义的虚函数。任何它的派生类都必须重新定义该函数。新定义该函数。第15页,本讲稿共24页抽象类:抽象类:(1)一个类中至少有一个纯虚函数,则称为一个类中至少有一个纯虚函数,则称为“抽象类抽象类”。(2)一个特殊的类
14、,为了抽象和设计的目的而建立的,它一个特殊的类,为了抽象和设计的目的而建立的,它处于继承层次结构的较高层。作为基类使用。处于继承层次结构的较高层。作为基类使用。(3)抽象类不能定义对象。抽象类不能定义对象。(4)抽象类的主要作用是将有关的特性组织在一个继承层次结抽象类的主要作用是将有关的特性组织在一个继承层次结构中,由它来提供一个公共的根,相关的子类从这个根派生构中,由它来提供一个公共的根,相关的子类从这个根派生出来。出来。第16页,本讲稿共24页例例 在在基基类类Shapes中中,将将成成员员display()声声明明为为纯纯虚虚函函数数,这这样样,基基类类Shapes就就是是一一个个抽抽象
15、象类类,这这时时我我们们无无法法声声明明Shapes类类的的对对象象,但但是是可可以以声声明明Shapes类类的的指指针针和和引引用用。Shapes类类经经过过公公有有派派生生产产生生了了Rectangle类类和和Circle类类。使使用用抽抽象象类类Shapes类类型型的的指指针针,当当它它指指向向某某个个派派生生类类的的对对象象时时,就就可可以以通通过过它它访问该对象的虚成员函数。访问该对象的虚成员函数。#include const double PI=3.14159;class Shapes/抽象基类抽象基类Shapes声明声明protected:int x,y;public:void
16、setvalue(int xx,int yy=0)x=xx;y=yy;virtual void display()=0;/纯虚函数成员纯虚函数成员;第17页,本讲稿共24页class Rectangle:public Shapes/派生类派生类Rectangle声明声明public:/虚成员函数虚成员函数void display()coutThe area of rectangle is:x*yendl;class Circle:public Shapes/派生类派生类Circle声明声明 public:/虚成虚成员员函数函数void display()coutThe area of circ
17、le is:PI*x*xdisplay();void funRef(Shapes&refs)refs.display();第18页,本讲稿共24页void main()Rectangle rect;Circle cir;rect.setvalue(20,20);cir.setvalue(10);func(&rect);funRef(cir);程序运行结果为:程序运行结果为:The area of rectangle is:400The area of circle is:314.159第19页,本讲稿共24页使用抽象类时需注意以下几点:使用抽象类时需注意以下几点:(1)抽象类只能用作其他类的基
18、类,不能建立抽象类对象。抽象类只能用作其他类的基类,不能建立抽象类对象。(2)抽抽象象类类不不能能用用作作参参数数类类型型(指指针针和和引引用用是是可可以以的的)、函函数数返返回回值值或显式转换的类型。或显式转换的类型。(3)可以声明一个抽象类的指针和引用。可以声明一个抽象类的指针和引用。练习练习4.2:已知有图形三角形和正方形已知有图形三角形和正方形,三角形有三个顶点的位置、填充颜三角形有三个顶点的位置、填充颜色等属性,有计算面积和周长的方法;正方形有中心点的位置、填充颜色、色等属性,有计算面积和周长的方法;正方形有中心点的位置、填充颜色、边长等属性,有计算面积和周长的方法。要求创建一个图形
19、的抽象类并且边长等属性,有计算面积和周长的方法。要求创建一个图形的抽象类并且其中只包含纯虚函数的方式来实现求面积、周长以及显示图形具体信息的其中只包含纯虚函数的方式来实现求面积、周长以及显示图形具体信息的功能。功能。第20页,本讲稿共24页多态性:多态性:建立在虚函数的概念和方法基础上。建立在虚函数的概念和方法基础上。表示:表示:发出同样的消息被不同类型的对象接受时导致完全不同的发出同样的消息被不同类型的对象接受时导致完全不同的行为行为。即同样的一个类的成员函数的调用,实现的不同性由接受对象自己。即同样的一个类的成员函数的调用,实现的不同性由接受对象自己确定。对象根据所接受到的消息而作出相应的
20、动作。确定。对象根据所接受到的消息而作出相应的动作。封装性、继承性、多态性构成了面向对象的程序设计的三大特征。封装性、继承性、多态性构成了面向对象的程序设计的三大特征。封装性是基础;继承性是关键;多态性是补充,封装性是基础;继承性是关键;多态性是补充,而多态又必须存在而多态又必须存在于继承的环境中。于继承的环境中。第21页,本讲稿共24页应用举例应用举例定义一个链表类定义一个链表类CList实现对矩形(长,宽,左实现对矩形(长,宽,左上角的位置)和圆(圆心、半径)数据进行插入和上角的位置)和圆(圆心、半径)数据进行插入和显示周长、面积的操作。显示周长、面积的操作。第22页,本讲稿共24页#in
21、clude Shape.htypedef struct NodeCShape*pShape;Node*pNext;TNode;class TList public:TList()pHead=NULL;Node*GetHead()return pHead;virtual TList()TNode*p=pHead,*q;while(p)delete p-pShape;q=p;p=p-pNext;delete q;第23页,本讲稿共24页void InsertShape(CShape*pShape)TNode*p=pHead,*s=new TNode;s-pNext=NULL;s-pShape=pS
22、hape;if(pHead=NULL)pHead=s;return;while(p-pNext)p=p-pNext;p-pNext=s;CString PrintContent()CString strCont=链表内容链表内容:;TNode*p=pHead;while(p)CString strFormat=;strFormat.Format(名称:名称:%s,面积面积:%.3f,周长周长:%.3f;t,p-pShape-GetShapeName(),p-pShape-Area(),p-pShape-Perimeter();strCont+=strFormat;p=p-pNext;return strCont;private:TNode*pHead;第24页,本讲稿共24页