面向对象程序设计之多态性与虚函数课件.ppt
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_1.gif)
![资源得分’ title=](/images/score_05.gif)
《面向对象程序设计之多态性与虚函数课件.ppt》由会员分享,可在线阅读,更多相关《面向对象程序设计之多态性与虚函数课件.ppt(63页珍藏版)》请在淘文阁 - 分享文档赚钱的网站上搜索。
1、面向对象程序设计之面向对象程序设计之多态性与虚函数多态性与虚函数第1页,此课件共63页哦6.1 多态性的概念多态性的概念多态性多态性(polymorphism)是面向对象程序设计的重要是面向对象程序设计的重要特征。一个算法语言如果只支持类,而不支持多态,只能说特征。一个算法语言如果只支持类,而不支持多态,只能说是基于对象的语言,如是基于对象的语言,如Ada,VB。C+支持多态性,在支持多态性,在C+程序设计中能够实现多态性。利用多态性,可以设计和扩展程序设计中能够实现多态性。利用多态性,可以设计和扩展一个易于扩展的系统。一个易于扩展的系统。l什么叫多态?什么叫多态?u多态的意思是一种事物的多种
2、形态。多态的意思是一种事物的多种形态。u在在C+中,中,是指具有不同功能的函数可以用同一个函数名是指具有不同功能的函数可以用同一个函数名。u面向对象方法中一般是这样描述多态性的:向不同的对象面向对象方法中一般是这样描述多态性的:向不同的对象发送同一个消息,不同的对象在接收时会产生不同的行为发送同一个消息,不同的对象在接收时会产生不同的行为(即方法)。(即方法)。第2页,此课件共63页哦写出程序运行结果写出程序运行结果#include#include Using namespace std;class studentpublic:student(int n,string nam,float s)
3、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 display()cout“num:”num “name:”name “score:”score “pay:”paydisplay();pt
4、=&g1;pt-display();第3页,此课件共63页哦6.1 多态性的概念多态性的概念我们其实已经接触过多态性的现象。如函数的重载我们其实已经接触过多态性的现象。如函数的重载多态性分类多态性分类:从系统实现的角度看,多态性分为以下两类:从系统实现的角度看,多态性分为以下两类:u静态多态性:又称编译时的多态性。如函数重载属于静态静态多态性:又称编译时的多态性。如函数重载属于静态多态性。多态性。u动态多态性:有称为运行时的多态性。它主要表现为虚函动态多态性:有称为运行时的多态性。它主要表现为虚函数数(virtual function)。第4页,此课件共63页哦6.3 虚函数虚函数能否用一个调
5、用形式,既能调用派生类的函数,又能调能否用一个调用形式,既能调用派生类的函数,又能调用基类同名函数?用基类同名函数?C+中的虚函数就是用来解决这一问题。中的虚函数就是用来解决这一问题。l虚函数的作用虚函数的作用:虚函数的作用是允许在派生类中重新定义与:虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。和派生类中的同名函数。第5页,此课件共63页哦6.3 虚函数虚函数#include#include class studentpublic:student(int n,string n
6、am,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 display()cout“num:”num “name:”name “score:”score “pay:”payd
7、isplay();/指向基类对象指向基类对象s1 pt=&g1;pt-display();/指向派生类对象指向派生类对象g1,仅输出仅输出了派生类的基类数据成员,因为它调用的是基了派生类的基类数据成员,因为它调用的是基类成员函数类成员函数display!假如想输出派生类的全部数据,当然可以采用下面两种方法之一:假如想输出派生类的全部数据,当然可以采用下面两种方法之一:u通过派生类对象名通过派生类对象名g1,调用派生类对象的成员函数:,调用派生类对象的成员函数:g1.display();u定义一个指向派生类的指针定义一个指向派生类的指针ptr,并指向,并指向g1,然后用,然后用ptr-displ
8、ay()。第6页,此课件共63页哦6.3 虚函数虚函数 我们可以用虚函数可以顺利解决这一问题。方法是:我们可以用虚函数可以顺利解决这一问题。方法是:u在基类在基类student 中声明中声明 display函数时,在最左边加上一个关键字函数时,在最左边加上一个关键字virtual:virtual void display();就可以就可以student类的类的display函数声明为虚函数,程序其它部分不变编译运行后,函数声明为虚函数,程序其它部分不变编译运行后,可见,使用可见,使用pt-display(),的确将,的确将graduate类对象类对象 g1 的全部数据显示了出来,的全部数据显示
9、了出来,说明它调用的是说明它调用的是g1 的成员函数的成员函数 display。u在派生类中重新定义该函数,要求函数名、函数类型、参数表完全相同。但不加在派生类中重新定义该函数,要求函数名、函数类型、参数表完全相同。但不加virtual关键字。关键字。u只在类里的成员函数声明时,加上关键字只在类里的成员函数声明时,加上关键字virtual,在类外定义定义虚函数时,不加,在类外定义定义虚函数时,不加virtual关键字。关键字。u定义一个指向定义一个指向基类对象基类对象的指针,并使她指向同一类中的某一对象;的指针,并使她指向同一类中的某一对象;u通过该指针标量调用此虚函数,此时调用的就是指针变量
10、指向的对象的同名函数,而不是基通过该指针标量调用此虚函数,此时调用的就是指针变量指向的对象的同名函数,而不是基类的同名函数!类的同名函数!第7页,此课件共63页哦6.3 虚函数虚函数通过使用虚函数和指针,就能方便地调用同一类族中不同通过使用虚函数和指针,就能方便地调用同一类族中不同类对象的同名函数,只要先用基类指针指向该对象即可。类对象的同名函数,只要先用基类指针指向该对象即可。#include#include class studentpublic:student(int n,string nam,float s)num=n;name=nam;score=s;virtual void dis
11、play()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 display()cout“num:”num “name:”name “score:”score “pay:”paydisplay();/指向基类对象指向基类对象s1 pt=&g1;pt-display
12、();/指向派生类对象指向派生类对象g1,调用调用g1的显示函数的显示函数display,打印出,打印出g1全部数据成员全部数据成员第8页,此课件共63页哦6.3 虚函数虚函数将函数重载与虚函数比较,可见:将函数重载与虚函数比较,可见:u函数重载是解决的是同一层次上的同名函数的问题。是横函数重载是解决的是同一层次上的同名函数的问题。是横向重载。向重载。u虚函数解决的是不同派生层次上的同名函数的问题。相当虚函数解决的是不同派生层次上的同名函数的问题。相当于纵向重载。于纵向重载。u同一类族的虚函数的首部是相同的;而重载函数的首部不同一类族的虚函数的首部是相同的;而重载函数的首部不相同(参数表不能相
13、同)。相同(参数表不能相同)。第9页,此课件共63页哦6.3 虚函数虚函数l静态关联与动态关联静态关联与动态关联C+在编译或运行时,对多个同名函数究竟调用哪一个在编译或运行时,对多个同名函数究竟调用哪一个函数,需要一定的机制来确定。这种确定调用的具体对象的函数,需要一定的机制来确定。这种确定调用的具体对象的过程称为过程称为“关联关联(binding)”,即把函数名与某一个类对象捆,即把函数名与某一个类对象捆绑在一起。绑在一起。函数重载,在编译时就可确定其调用的函数是哪一个;函数重载,在编译时就可确定其调用的函数是哪一个;通过对象名调用虚函数,在编译时也可确定其调用的虚函数通过对象名调用虚函数,
14、在编译时也可确定其调用的虚函数属于哪一个类。其过程称为属于哪一个类。其过程称为“静态关联静态关联(static binding),因为是在运行前进行关联的,又成为早期关联因为是在运行前进行关联的,又成为早期关联(early binding)。第10页,此课件共63页哦6.3 虚函数虚函数通过指针和虚函数的结合,在编译阶段是没法进行关联的,通过指针和虚函数的结合,在编译阶段是没法进行关联的,因为编译只作静态的语法检查,光从语句形式因为编译只作静态的语法检查,光从语句形式pt-display()无无法确定调用的对象,也就没法关联。法确定调用的对象,也就没法关联。出现这种情况,我们可以在运行阶段来处
15、理关联。在运行出现这种情况,我们可以在运行阶段来处理关联。在运行阶段,基类指针先指向某一个对象,然后通过指针调用该对象的阶段,基类指针先指向某一个对象,然后通过指针调用该对象的成员函数。此时调用哪个函数是确定的,没有不确定因素。例如成员函数。此时调用哪个函数是确定的,没有不确定因素。例如语句语句 pt=&g1;pt-display();非常确定的是调用非常确定的是调用g1对象的成员对象的成员函数函数display。这种情况由于是在运行阶段将虚函数与某一类对象这种情况由于是在运行阶段将虚函数与某一类对象“绑定绑定”在一起的,因此称为在一起的,因此称为“动态关联动态关联(dynamic bindi
16、ng),或,或“滞后关联滞后关联(late binding)”。第11页,此课件共63页哦6.3 虚函数虚函数l使用虚函数,要注意使用虚函数,要注意u只能用只能用virtual 声明类的成员函数,类外的普通函数不能声明类的成员函数,类外的普通函数不能声明成虚函数,因为它没有继承的操作。声明成虚函数,因为它没有继承的操作。u一个成员函数被声明成虚函数后,在同一类族中的类就不一个成员函数被声明成虚函数后,在同一类族中的类就不能再定义一个非能再定义一个非virtual的、但与该函数具有相同参数表和的、但与该函数具有相同参数表和返回类型的同名函数。返回类型的同名函数。u使用虚函数,系统要有一定的空间开
17、销。当一个类带有虚使用虚函数,系统要有一定的空间开销。当一个类带有虚函数时,编译系统会为该类构造一个虚函数表函数时,编译系统会为该类构造一个虚函数表(virtual function table,vtable),它是一个指针数组,存放每个,它是一个指针数组,存放每个虚函数的入口地址。虚函数的入口地址。系统在进行动态关联时的时间开销是很少的,所以多态系统在进行动态关联时的时间开销是很少的,所以多态性运行效率非常高。性运行效率非常高。第12页,此课件共63页哦6.3 虚函数虚函数l什么情况下使用虚函数?什么情况下使用虚函数?u成员函数所在的类是否会作为基类?成员函数被继承后有成员函数所在的类是否会
18、作为基类?成员函数被继承后有没有可能发生功能变化,如果两个因素都具备,就应该将没有可能发生功能变化,如果两个因素都具备,就应该将它声明成虚函数。它声明成虚函数。u如果成员函数被继承后功能不变,或派生类用不到该函数,如果成员函数被继承后功能不变,或派生类用不到该函数,就不要声明成虚函数。就不要声明成虚函数。u应考虑对成员函数的访问是通过对象名还是基类指针,如应考虑对成员函数的访问是通过对象名还是基类指针,如果是通过基类指针或引用访问,则应当声明为虚函数。果是通过基类指针或引用访问,则应当声明为虚函数。u有时基类中定义虚函数时并不定义它的函数体,即函数体有时基类中定义虚函数时并不定义它的函数体,即
19、函数体为空。其作用只是定义了一个虚函数名,具体功能留给派为空。其作用只是定义了一个虚函数名,具体功能留给派生类添加(生类添加(6.4 节会讨论这种情况)。节会讨论这种情况)。第13页,此课件共63页哦6.3 虚函数虚函数l虚析构函数虚析构函数u问题的引出:我们知道,当派生类对象撤消时,系统先调用派生类析构函数,再调用问题的引出:我们知道,当派生类对象撤消时,系统先调用派生类析构函数,再调用基类析构函数。但是,如果用基类析构函数。但是,如果用new 运算符建立了一个派生类临时对象,但用一个基类运算符建立了一个派生类临时对象,但用一个基类指针指向它,当程序用带指针参数的指针指向它,当程序用带指针参
20、数的delete 撤消对象时,会发生让人不能接受的情撤消对象时,会发生让人不能接受的情况:系统只析构基类对象,而不析构派生类对象:况:系统只析构基类对象,而不析构派生类对象:class pointpublic:point()point()cout“析构基类对象析构基类对象”endl;class circle:public pointpublic:circle()circle()cout“析构派生类对象析构派生类对象”endl;private:int radius;int main()point*p=new circle;/指针为指向基类对象指针,指针为指向基类对象指针,/但实际指向临时派生类对
21、象但实际指向临时派生类对象 delete p;return 0;第14页,此课件共63页哦6.3 虚函数虚函数实际上,程序只析构了基类对象,而没有析构派生类对实际上,程序只析构了基类对象,而没有析构派生类对象,为什么呢?因为指针象,为什么呢?因为指针p 为基类指针,系统认为它只有基为基类指针,系统认为它只有基类对象,而与派生类对象无关。实际上,由于该指针被指向类对象,而与派生类对象无关。实际上,由于该指针被指向了一个临时派生类对象,所以还应该这个临时的析构派生类了一个临时派生类对象,所以还应该这个临时的析构派生类对象。对象。u解决的办法:解决的办法:可以将基类的析构函数声明为虚析构函数。可以将
22、基类的析构函数声明为虚析构函数。如:如:virtual point()cout“析构基类对象析构基类对象”endl;程序其它部分不动,就行了。程序其它部分不动,就行了。当基类的析构函数被定义成当基类的析构函数被定义成virtual,无论指针指向同一,无论指针指向同一类族中的哪一个对象,当撤消对象时,系统会采用动态关联,类族中的哪一个对象,当撤消对象时,系统会采用动态关联,调用相应的析构函数,清理该对象,然后再析构基类对象。调用相应的析构函数,清理该对象,然后再析构基类对象。第15页,此课件共63页哦6.4 纯虚函数与抽象类纯虚函数与抽象类前面已经提到,有时在基类中将某一成员函数定为虚函前面已经
23、提到,有时在基类中将某一成员函数定为虚函数并不是基类本身的需要,而是派生类的需要。在基类中预数并不是基类本身的需要,而是派生类的需要。在基类中预留一个函数名,具体功能留给派生类根据需要去定义。留一个函数名,具体功能留给派生类根据需要去定义。在上一节中基类在上一节中基类point 中有定义面积中有定义面积area函数,是因为函数,是因为“点点”没有面积的概念。但是,其直接派生类没有面积的概念。但是,其直接派生类circle和间接和间接派生类派生类cylinder却都需要却都需要area 函数,而且这两个函数,而且这两个area 函数的函数的功能还不相同,一个是求圆面积,一个是求圆柱体表面积。功能
24、还不相同,一个是求圆面积,一个是求圆柱体表面积。也许会想到,在基类也许会想到,在基类point 中加一个中加一个area 函数,并声明函数,并声明为虚函数:为虚函数:virtual float area()const return 0;其返回其返回0表示表示“点点”没有面积。其实,在基类中并不使没有面积。其实,在基类中并不使用这个函数,其返回值也没有意义。用这个函数,其返回值也没有意义。第16页,此课件共63页哦6.4 纯虚函数与抽象类纯虚函数与抽象类为简化起见,可以不写出这种无意义的函数体,只给出为简化起见,可以不写出这种无意义的函数体,只给出函数的原型,并在后面加上函数的原型,并在后面加上
25、“=0”,如:,如:virtual float area()const=0;/纯虚函数纯虚函数这就将这就将area声明为一个纯虚函数声明为一个纯虚函数(pure virtual function)l纯虚函数的声明形式纯虚函数的声明形式virtual 函数类型函数类型 函数名函数名(参数表参数表)=0;说明说明u纯虚函数没有函数体;纯虚函数没有函数体;u最后的最后的“=0”不表示函数值返回不表示函数值返回0,它只是形式上的作用,它只是形式上的作用,告诉编译系统:这是纯虚函数,告诉编译系统:这是纯虚函数,u这是一个声明语句,以分号结尾。这是一个声明语句,以分号结尾。u如果基类中声明了纯虚函数,但派
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 面向 对象 程序设计 多态性 函数 课件
![提示](https://www.taowenge.com/images/bang_tan.gif)
限制150内