面向对象程序设计之多态性与虚函数.ppt
《面向对象程序设计之多态性与虚函数.ppt》由会员分享,可在线阅读,更多相关《面向对象程序设计之多态性与虚函数.ppt(66页珍藏版)》请在三一办公上搜索。
1、Object-OrientedProgramming in C+第六章 多态性与虚函数,6.1 多态性的概念,多态性(polymorphism)是面向对象程序设计的重要特征。一个算法语言如果只支持类,而不支持多态,只能说是基于对象的语言,如Ada,VB。C+支持多态性,在C+程序设计中能够实现多态性。利用多态性,可以设计和扩展一个易于扩展的系统。什么叫多态?多态的意思是一种事物的多种形态。在C+中,是指具有不同功能的函数可以用同一个函数名。面向对象方法中一般是这样描述多态性的:向不同的对象发送同一个消息,不同的对象在接收时会产生不同的行为(即方法)。,写出程序运行结果,#include#inc
2、lude Using namespace std;class studentpublic:student(int n,string nam,float s)num=n;name=nam;score=s;void display()cout“num:”num“name:”name“score:”scoreendl;protected:int num;string name;float score;class graduate:public studentpublic:graduate(int n,string nam,float s,float p):student(n,nam,s),pay(p
3、),void display()coutdisplay();pt=,6.1 多态性的概念,我们其实已经接触过多态性的现象。如函数的重载多态性分类:从系统实现的角度看,多态性分为以下两类:静态多态性:又称编译时的多态性。如函数重载属于静态多态性。动态多态性:有称为运行时的多态性。它主要表现为虚函数(virtual function)。,6.3 虚函数,能否用一个调用形式,既能调用派生类的函数,又能调用基类同名函数?C+中的虚函数就是用来解决这一问题。虚函数的作用:虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。,6.3 虚函数,#i
4、nclude#include class studentpublic:student(int n,string nam,float s)num=n;name=nam;score=s;void display()cout“num:”num“name:”name“score:”scoreendl;protected:int num;string name;float score;class graduate:public studentpublic:graduate(int n,string nam,float s,float p):student(n,nam,s),pay(p),void dis
5、play()coutdisplay();/指向基类对象s1 pt=/指向派生类对象g1,仅输出了派生类的基类数据成员,因为它调用的是基类成员函数display!,假如想输出派生类的全部数据,当然可以采用下面两种方法之一:通过派生类对象名g1,调用派生类对象的成员函数:g1.display();定义一个指向派生类的指针ptr,并指向g1,然后用ptr-display()。,6.3 虚函数,我们可以用虚函数可以顺利解决这一问题。方法是:在基类student 中声明 display函数时,在最左边加上一个关键字virtual:virtual void display();就可以student类的di
6、splay函数声明为虚函数,程序其它部分不变编译运行后,可见,使用pt-display(),的确将graduate类对象 g1 的全部数据显示了出来,说明它调用的是g1 的成员函数 display。在派生类中重新定义该函数,要求函数名、函数类型、参数表完全相同。但不加virtual关键字。只在类里的成员函数声明时,加上关键字virtual,在类外定义定义虚函数时,不加virtual关键字。定义一个指向基类对象的指针,并使她指向同一类中的某一对象;通过该指针标量调用此虚函数,此时调用的就是指针变量指向的对象的同名函数,而不是基类的同名函数!,6.3 虚函数,通过使用虚函数和指针,就能方便地调用同
7、一类族中不同类对象的同名函数,只要先用基类指针指向该对象即可。,#include#include class studentpublic:student(int n,string nam,float s)num=n;name=nam;score=s;virtual void display()cout“num:”num“name:”name“score:”scoreendl;protected:int num;string name;float score;class graduate:public studentpublic:graduate(int n,string nam,float s
8、,float p):student(n,nam,s),pay(p),void display()coutdisplay();/指向基类对象s1 pt=/指向派生类对象g1,调用g1的显示函数display,打印出g1全部数据成员,6.3 虚函数,将函数重载与虚函数比较,可见:函数重载是解决的是同一层次上的同名函数的问题。是横向重载。虚函数解决的是不同派生层次上的同名函数的问题。相当于纵向重载。同一类族的虚函数的首部是相同的;而重载函数的首部不相同(参数表不能相同)。,6.3 虚函数,静态关联与动态关联C+在编译或运行时,对多个同名函数究竟调用哪一个函数,需要一定的机制来确定。这种确定调用的具体
9、对象的过程称为“关联(binding)”,即把函数名与某一个类对象捆绑在一起。函数重载,在编译时就可确定其调用的函数是哪一个;通过对象名调用虚函数,在编译时也可确定其调用的虚函数属于哪一个类。其过程称为“静态关联(static binding),因为是在运行前进行关联的,又成为早期关联(early binding)。,6.3 虚函数,通过指针和虚函数的结合,在编译阶段是没法进行关联的,因为编译只作静态的语法检查,光从语句形式pt-display()无法确定调用的对象,也就没法关联。出现这种情况,我们可以在运行阶段来处理关联。在运行阶段,基类指针先指向某一个对象,然后通过指针调用该对象的成员函数
10、。此时调用哪个函数是确定的,没有不确定因素。例如语句 pt=非常确定的是调用g1对象的成员函数display。这种情况由于是在运行阶段将虚函数与某一类对象“绑定”在一起的,因此称为“动态关联(dynamic binding),或“滞后关联(late binding)”。,6.3 虚函数,使用虚函数,要注意只能用virtual 声明类的成员函数,类外的普通函数不能声明成虚函数,因为它没有继承的操作。一个成员函数被声明成虚函数后,在同一类族中的类就不能再定义一个非virtual的、但与该函数具有相同参数表和返回类型的同名函数。使用虚函数,系统要有一定的空间开销。当一个类带有虚函数时,编译系统会为该
11、类构造一个虚函数表(virtual function table,vtable),它是一个指针数组,存放每个虚函数的入口地址。系统在进行动态关联时的时间开销是很少的,所以多态性运行效率非常高。,6.3 虚函数,什么情况下使用虚函数?成员函数所在的类是否会作为基类?成员函数被继承后有没有可能发生功能变化,如果两个因素都具备,就应该将它声明成虚函数。如果成员函数被继承后功能不变,或派生类用不到该函数,就不要声明成虚函数。应考虑对成员函数的访问是通过对象名还是基类指针,如果是通过基类指针或引用访问,则应当声明为虚函数。有时基类中定义虚函数时并不定义它的函数体,即函数体为空。其作用只是定义了一个虚函数
12、名,具体功能留给派生类添加(6.4 节会讨论这种情况)。,6.3 虚函数,虚析构函数问题的引出:我们知道,当派生类对象撤消时,系统先调用派生类析构函数,再调用基类析构函数。但是,如果用new 运算符建立了一个派生类临时对象,但用一个基类指针指向它,当程序用带指针参数的delete 撤消对象时,会发生让人不能接受的情况:系统只析构基类对象,而不析构派生类对象:,class pointpublic:point()point()cout“析构基类对象”endl;class circle:public pointpublic:circle()circle()cout“析构派生类对象”endl;,pri
13、vate:int radius;int main()point*p=new circle;/指针为指向基类对象指针,/但实际指向临时派生类对象 delete p;return 0;,6.3 虚函数,实际上,程序只析构了基类对象,而没有析构派生类对象,为什么呢?因为指针p 为基类指针,系统认为它只有基类对象,而与派生类对象无关。实际上,由于该指针被指向了一个临时派生类对象,所以还应该这个临时的析构派生类对象。解决的办法:可以将基类的析构函数声明为虚析构函数。如:virtual point()cout“析构基类对象”endl;程序其它部分不动,就行了。当基类的析构函数被定义成virtual,无论指
14、针指向同一类族中的哪一个对象,当撤消对象时,系统会采用动态关联,调用相应的析构函数,清理该对象,然后再析构基类对象。,6.4 纯虚函数与抽象类,前面已经提到,有时在基类中将某一成员函数定为虚函数并不是基类本身的需要,而是派生类的需要。在基类中预留一个函数名,具体功能留给派生类根据需要去定义。在上一节中基类point 中有定义面积area函数,是因为“点”没有面积的概念。但是,其直接派生类circle和间接派生类cylinder却都需要area 函数,而且这两个area 函数的功能还不相同,一个是求圆面积,一个是求圆柱体表面积。也许会想到,在基类point 中加一个area 函数,并声明为虚函数
15、:virtual float area()const return 0;其返回0表示“点”没有面积。其实,在基类中并不使用这个函数,其返回值也没有意义。,6.4 纯虚函数与抽象类,为简化起见,可以不写出这种无意义的函数体,只给出函数的原型,并在后面加上“=0”,如:virtual float area()const=0;/纯虚函数这就将area声明为一个纯虚函数(pure virtual function)纯虚函数的声明形式virtual 函数类型 函数名(参数表)=0;说明纯虚函数没有函数体;最后的“=0”不表示函数值返回0,它只是形式上的作用,告诉编译系统:这是纯虚函数,这是一个声明语句,
16、以分号结尾。如果基类中声明了纯虚函数,但派生类中定义该函数,则该虚函数在派生类中仍为纯虚函数。,6.4 纯虚函数与抽象类,纯虚函数的作用是在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它进行定义。如果基类中没有保留函数名,则无法实现多态性。,6.4 纯虚函数与抽象类,抽象类什么叫抽象类?一般声明了一个类,用来定义若干对象。但有些类并不是用来生成对象,而是作为基类去建立派生类。这种不用来定义对象,而只作为一种基本类型用做继承的类,就叫抽象类(abstract class)。由于抽象类常作为基类,我们也称为抽象基类(abstract base class)。比如,凡是包含纯虚函数的类都
17、叫抽象类。因为纯虚函数不能被调用,包含纯虚函数的类无法建立对象。抽象类的作用:是作为一个类族的共同基类。即,为一个类族提供一个公共接口。一个类层次结构中可以不包含任何抽象类,每一层次的类都可以建立对象。但是,许多系统的顶层是一个抽象类,甚至顶部有好几层都是抽象类。,6.4 纯虚函数与抽象类,如果由抽象类所派生出的新类中对基类的所有纯虚函数都进行了定义,这个派生类就不是抽象类,可以被调用,成为可以用来定义对象的具体类(concrete class)。如果由抽象类所派生出的新类中对基类的所有纯虚函数都没有进行定义,这个派生类就仍然是抽象类。虽然抽象类不能定义对象,但可以定义指向抽象类的数据的指针。
18、当派生类成为具体类之后,就可以用这种指针向派生类对象,然后通过该指针调用虚函数,实现多态性操作。,编程练习:定义虚函数,基类为SHAPE,派生出圆形、矩形、三角形类。,#include using namespace std;/定义抽象基类Shapeclass Shapepublic:/定义纯虚函数;/定义Circle类class Circle:public Shapepublic:Circle(double r):radius(r)/定义虚函数 protected:double radius;/半径;,/定义Rectangle类class Rectangle:public Shapepubl
19、ic:Rectangle(double w,double h):width(w),height(h)/定义虚函数 protected:double width,height;class Triangle:public Shapepublic:Triangle(double w,double h):width(w),height(h)/定义虚函数 protected:double width,height;,#include using namespace std;/定义抽象基类Shapeclass Shapepublic:virtual double area()const=0;/纯虚函数;/
20、定义Circle类class Circle:public Shapepublic:Circle(double r):radius(r)virtual double area()const return 3.14159*radius*radius;/定义虚函数 protected:double radius;/半径;,/定义Rectangle类class Rectangle:public Shapepublic:Rectangle(double w,double h):width(w),height(h)virtual double area()const return width*height
21、;/定义虚函数 protected:double width,height;class Triangle:public Shapepublic:Triangle(double w,double h):width(w),height(h)virtual double area()const return 0.5*width*height;/定义虚函数 protected:double width,height;,续:定义一个函数printArea,以对象为形参,输出三种形状的面积,int main()Circle circle(12.6);/建立Circle类对象circle coutarea
22、of circle=;printArea(circle);/输出circle的面积 Rectangle rectangle(4.5,8.4);/建立Rectangle类对象rectangle coutarea of rectangle=;printArea(rectangle);/输出rectangle的面积 Triangle triangle(4.5,8.4);/建立Triangle类对象 coutarea of triangle=;printArea(triangle);/输出triangle的面积 return 0;,/输出面积的函数void printArea(const Shape,
23、6.4 纯虚函数与抽象类,多态性把操作细节留给类的设计者(专业人员)去完成,而让编程人员(类的使用者)只需做一些宏观性的工作,告诉系统做什么,不必考虑怎么做,简化了应用程序的编码工作。因此有人说,多态性是开启继承功能的钥匙。,Object-OrientedProgramming in C+第七章 输入输出流,第一章 C+的初步知识第二章 类和对象第三章 再论类和对象第四章 运算符重载第五章 继承与派生第六章 多态性与虚函数第七章 输入输出流第八章 C+工具,7.1 C+的输入与输出7.2 标准输出流7.3 标准出入流7.4 文件操作与文件流7.5 字符串流,7.1 C+的输入和输出,输入输出的
24、含义:从操作系统角度看,每一个与主机相连的输入输出设备都被看做一个文件。终端键盘是输入文件,终端显示器是输出文件。磁盘或光盘也可以被看作是输入输出文件。程序的输入:指的是从输入文件将数据传送给程序;程序的输出:指的是从程序将数据输出给输出文件。C+的输入输出包括以下三个方面的内容:标准设备输入输出,从键盘输入。输出到显示器。简称标准I/O。以外存储器文件为对象的输入输出。指从磁盘文件中输入数据,将数据输出到磁盘文件中。简称文件I/O对内存中指定的空间进行输入输出。通常指定一个字符数组作为存储空间,它称为字符串输入输出,简称串I/OC+采取了不同的方法,实现这三种输入输出。,7.1 C+的输入和
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 面向 对象 程序设计 多态性 函数
链接地址:https://www.31ppt.com/p-6213418.html