c++7 多态性PPT课件_第1页
c++7 多态性PPT课件_第2页
c++7 多态性PPT课件_第3页
c++7 多态性PPT课件_第4页
c++7 多态性PPT课件_第5页
已阅读5页,还剩49页未读 继续免费阅读

下载本文档

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

文档简介

1、2021/3/91第7章 多态性与虚函数7.1 多态性的概念7.2 静态关联与动态关联7.3 虚函数7.4 纯虚函数与抽象类2021/3/92在C+程序设计中,多态性是指具有不同功能的函数可以用同一个函数名,这样就可以用一个函数名调用不同内容的函数。7.1 多态性的概念2021/3/93多态的实现多态的实现2021/3/94 函数重载是多态性的一种简单形式,它是指允许在相同的函数重载是多态性的一种简单形式,它是指允许在相同的作用域内,相同的函数名对应着不同的实现。作用域内,相同的函数名对应着不同的实现。 函数重载的条件是要求函数参数的类型或个数有所不同。函数重载的条件是要求函数参数的类型或个数

2、有所不同。对成员函数的重载有以下三种表达方式:对成员函数的重载有以下三种表达方式:1、在一个类中重载;、在一个类中重载;2、在不同类中重载;、在不同类中重载;3、基类的成员函数在派生类中重载。、基类的成员函数在派生类中重载。2021/3/95具有相同名字的重载函数是在编译时区分的,有以下三种区分具有相同名字的重载函数是在编译时区分的,有以下三种区分方法:方法:1、根据参数的特征加以区别,例如:、根据参数的特征加以区别,例如: show(int, char); show (char*, float);2、使用类作用域符、使用类作用域符“:”加以区分,例如:加以区分,例如: Circle:show

3、(); Point:show();3、根据类对象加以区分,例如:、根据类对象加以区分,例如: acirle.show()调用调用Circle:show() apoint.show()调用调用Point:show()2021/3/96 除了函数重载这种简单形式之外,除了函数重载这种简单形式之外,C+ 还提供了一还提供了一种更为灵活的特征机制种更为灵活的特征机制虚函数。虚函数。 虚函数允许函数调用与函数体的联系在运行时才给虚函数允许函数调用与函数体的联系在运行时才给出。出。当需要同一接口、多种实现时,这种功能显得尤其当需要同一接口、多种实现时,这种功能显得尤其重要。重要。 在讲述虚函数的概念之前,

4、先介绍子类型及静态联在讲述虚函数的概念之前,先介绍子类型及静态联编和动态联编的相关内容。编和动态联编的相关内容。2021/3/97(动态多态)要研究的问题是:当一个基类被继承为不同的派生类时,各派生类可以使用与基类成员相同的成员名,如果在运行时用同一个成员名调用类对象的成员,会调用哪个对象的成员?2021/3/98举例举例 根据类型适应性,在公有继承方式下,指向基根据类型适应性,在公有继承方式下,指向基类和派生类的指针变量是相关的。如果类和派生类的指针变量是相关的。如果B 是基是基类,类,D 是从是从B 公有派生出来的派生类,则在公有派生出来的派生类,则在C+ 中,指向基类中,指向基类B 的指

5、针的指针P 也可以指向派生也可以指向派生类类D。 当当P 指向派生类指向派生类D 的对象时,利用指针的对象时,利用指针P 可以可以访问从基类访问从基类B 继承的成员,但派生类继承的成员,但派生类D 自己定自己定义的成员不能用义的成员不能用P 访问访问(除非用显式类型转换除非用显式类型转换)。2021/3/99 例如:下面是指向基类对象的指针指向派生类对象,而访问从例如:下面是指向基类对象的指针指向派生类对象,而访问从基类继承的成员的例子。基类继承的成员的例子。 #include #include class B char name80; public: void put_name(char *

6、s) strcpy(name,s); void show_name() coutname“n”; ;2021/3/910class D: public B char phone_num80; public: void put_phone(char *num) strcpy(phone_num,num); void show_phone() coutphone_numput_name(“Zhang Fang”); p=&Dobj; p-put_name(“Wang Ming”); /访问从基类继承的成员函数访问从基类继承的成员函数 Bobj.show_name(); Dobj.show_name

7、(); dp=&Dobj; dp-put_phone(“83768493”); /访问自己的成员访问自己的成员 dp-show_phone(); p-show_phone(); /error / 指向基类指针不能访问派生类定指向基类指针不能访问派生类定 义的成员义的成员 (D *)P)-show_phone(); /强行类型转换强行类型转换2021/3/912Zhang FangWang Ming8376849383768493 注意:注意:希望用基类指针访问其公有派生类的特定成员,必希望用基类指针访问其公有派生类的特定成员,必须将基类指针用显式类型转换为派生类指针。根据类型适应须将基类指针用

8、显式类型转换为派生类指针。根据类型适应性的原则,一个指向基类的指针可用来指向以公有派生的任性的原则,一个指向基类的指针可用来指向以公有派生的任何对象是何对象是C+ 实现运行时多态性的关键。实现运行时多态性的关键。2021/3/913CommissionEmployeeclass CommissionEmployeepublic: CommissionEmployee( const string &, const string &, const string &, double = 0.0, double = 0.0 ); void setFirstName( const string & );

9、 / set first name string getFirstName() const; / return first name void setLastName( const string & ); / set last name string getLastName() const; / return last name void setSocialSecurityNumber( const string & ); / set SSN string getSocialSecurityNumber() const; / return SSN void setGrossSales( dou

10、ble ); / set gross sales amount double getGrossSales() const; / return gross sales amount void setCommissionRate( double ); / set commission rate double getCommissionRate() const; / return commission rate double earnings() const; / calculate earnings void print() const; / print CommissionEmployee ob

11、jectprivate: string firstName; string lastName; string socialSecurityNumber; double grossSales; / gross weekly sales double commissionRate; / commission percentage; / end class CommissionEmployee2021/3/914BasePlusCommissionEmployee class BasePlusCommissionEmployee : public CommissionEmployee public:

12、 BasePlusCommissionEmployee( const string &, const string &, const string &, double = 0.0, double = 0.0, double = 0.0 ); void setBaseSalary( double ); / set base salary double getBaseSalary() const; / return base salary double earnings() const; / calculate earnings void print() const; / print BasePl

13、usCommissionEmployee object private: double baseSalary; / base salary ; / end class BasePlusCommissionEmployee2021/3/915派生类指针指向基类对象 int main() CommissionEmployee commissionEmployee( Sue, Jones, 222-22-2222, 10000, .06 ); BasePlusCommissionEmployee *basePlusCommissionEmployeePtr = 0; / aim derived-cl

14、ass pointer at base-class object / Error: a CommissionEmployee is not a BasePlusCommissionEmployee basePlusCommissionEmployeePtr = &commissionEmployee; return 0; / end main2021/3/916基类对象指针指向派生类对象 这种情况下,要访问派生类的成员函数,要做: /强制类型转换 如: (BasePlusCommissionEmployee *) commissionEmployeePtr)-print(); 2021/3/9

15、17确定调用的具体对象的过程称为关联(binding)。在这里是指把一个函数名与一个类对象捆绑在一起,建立关联。一般地说,关联指把一个标识符和一个存储地址联系起来。7.2 静态关联与动态关联 2021/3/918在编译时即可确定其调用的函数,其过程称为静态关联(static binding),由于是在运行前进行关联的,故又称为早期关联(early binding)。在运行阶段把虚函数和类对象“绑定”在一起的,此过程称为动态关联(dynamic binding)。这种多态性是动态的多态性,即运行阶段的多态性。由于动态关联是在编译以后的运行阶段进行的,因此也称为滞后关联(late binding)

16、。2021/3/919多态性分为两类: 静态多态性和动态多态性。(1)静态多态: (例7-1)(在程序编译时系统就能决定调用的是哪个函数),因此静态多态性又称编译时的多态性。静态多态是通过函数重载来实现的。(2)动态多态(在例7-1基础上加virtual)是在程序运行过程中才动态地确定操作所针对的对象。它又称运行时的多态性。动态多态性是通过虚函数(virtual function)实现的。2021/3/920在类的继承层次结构中,在不同的层次中可以出现名字相同、参数个数和类型都相同而功能不同的函数。编译系统按照同名覆盖的原则决定调用的对象。例7.2 基类与派生类中有同名函数。7.3 虚函数 7

17、.3.1 虚函数的作用2021/3/921虚函数的作用: 允许在派生类中重新定义与基类同名的函数,并且通过基类指针或引用来访问基类和派生类中的同名函数,实现动态多态的效果。2021/3/922虚函数是一种非静态的成员函数,定义格式如下:虚函数是一种非静态的成员函数,定义格式如下:virtual () /其中,其中,virtual 是关键字。是关键字。 如果某个类中的一个成员函数被说明为虚函数,该成员函如果某个类中的一个成员函数被说明为虚函数,该成员函数可能在派生类中存在着不同的实现版本。数可能在派生类中存在着不同的实现版本。 由于存在有虚函数,编译器将进行动态联编,使调用虚函由于存在有虚函数,

18、编译器将进行动态联编,使调用虚函数的对象在运行时确定,以实现动态联编的多态性。数的对象在运行时确定,以实现动态联编的多态性。2021/3/923例如:例如:使用虚函数将上例改为动态联编的情况,将得到不同的使用虚函数将上例改为动态联编的情况,将得到不同的结果结果#include class Base protected: int x; public: Base(int a) x=a; virtual void print() cout“Base ”x“n”; ;2021/3/924class First_d: public Base public: First_d(int a): Base(a)

19、 virtual void print() cout“First derivationn”x“n”; ;class Second_d: public Base public: Second_d(int a)=Base(a) virtual void print() cout“Second derivationn”xprint(); p=&obj2; p-print(); p=&obj3; p-print(); obj2.print(); obj3.print(); 运行结果:运行结果: Base 1 First derivation 2 Second derivation 3 First de

20、rivation 2 Second derivation 3程序中,程序中,p-print();出现了三出现了三次,由于次,由于p 指向的对象不同,每次指向的对象不同,每次执行了执行了print() 的不同实现版本。的不同实现版本。2021/3/9261、在基类中,将该函数说明为虚函数、在基类中,将该函数说明为虚函数(virtual);2、定义基类的公有派生类;、定义基类的公有派生类;3、在基类的公有派生类中重载该虚函数;、在基类的公有派生类中重载该虚函数;4、定义指向基类的指针变量,它指向基类的公有派生类的对、定义指向基类的指针变量,它指向基类的公有派生类的对象。象。 重载虚函数不是一般的重

21、载函数,它要求函数名、返回重载虚函数不是一般的重载函数,它要求函数名、返回类型、参数个数、参数类型和顺序完全相同。类型、参数个数、参数类型和顺序完全相同。 由于对虚函数进行重载,因此,由于对虚函数进行重载,因此,在派生类中的虚函数前在派生类中的虚函数前的的virtual关键字可以省略。关键字可以省略。2021/3/927例如:例如:下面是下面是“单界面、多实现版本单界面、多实现版本”概念的另一个程序例子概念的另一个程序例子#include class figure protected: double x,y; public: void set_dim(double i; double j=0)

22、 x=i; y=j; virtual void show_area() cout“No area computation define ”; cout“for this class.n”; ;2021/3/928class triangle: public figure public: void show_area() cout“Triangle with high”; coutx“and base”y; cout“has an ares of”; coutx*0.5*y; ;class square: public figure public: void show_area() 2021/3

23、/929 cout“Square with dimension”; coutx“*”y; cout“has an area of”; coutx*y“n”; ;class circle: public figure public: void show_area() cout“Circle with radius”; coutx; cout“has an area of”; coutset_dim(10.0,5.0); p-show_area(); Triangle with high 10 and 5 has an area of 25.0Square with dimension 10*5

24、has an area of 50.0Circle with radius 9 has an area of 254.34 p= &s; p-set_dim(10.0,5.0); p-show_area(); p=&c; p-set_dim(9.0); p-show_area(); 2021/3/931虚函数的使用方法: (1) 在基类用virtual声明成员函数为虚函数。这样就可以在派生类中重新定义此函数,为它赋予新的功能,并能方便地被调用。注意事项: 在派生类中重新定义此函数,要求函数名、函数类型、函数参数个数和类型全部与基类的虚函数相同,并根据派生类的需要重新定义函数体。2021/3/9

25、32(2) 定义一个指向基类对象的指针变量或对象的引用,并使它指向同一类族中需要调用该函数的对象。(即使用赋值兼容规则)(3) 通过该指针变量或对象的引用调用此虚函数。示例:指针调用,引用调用2021/3/933使用虚函数时,有几点要注意: (1)(1)只能用只能用virtualvirtual声明类的成员函数,使它成为虚声明类的成员函数,使它成为虚函数函数,而不能将类外的普通函数声明为虚函数。因,而不能将类外的普通函数声明为虚函数。因为虚函数的作用是允许在派生类中对基类的虚函数为虚函数的作用是允许在派生类中对基类的虚函数重新定义。重新定义。显然,它只能用于类的继承层次结构中显然,它只能用于类的

26、继承层次结构中。(2) (2) 一个成员函数被声明为虚函数后,在同一类族一个成员函数被声明为虚函数后,在同一类族中的类就不能再定义一个中的类就不能再定义一个非非virtualvirtual的但与该虚函的但与该虚函数具有相同的参数数具有相同的参数( (包括个数和类型包括个数和类型) )和函数返回值和函数返回值类型的同名函数。类型的同名函数。(3)(3)构造函数不能声明为虚函数构造函数不能声明为虚函数。这是因为在执行。这是因为在执行构造函数时类对象还未完成建立过程,当然谈不上构造函数时类对象还未完成建立过程,当然谈不上函数与类对象的绑定。函数与类对象的绑定。2021/3/934(1) 首先看成员函

27、数所在的类是否会作为基类。然后看成员函数在类的继承后有无可能被更改功能,如果希望更改其功能的,一般应该将它声明为虚函数。如果成员函数在类被继承后功能不需修改,或派生类用不到该函数,则不要把它声明为虚函数。不要仅仅考虑到要作为基类而把类中的所有成员函数都声明为虚函数。7.3.2 在什么情况下应当声明虚函数2021/3/935(2) 应考虑对成员函数的调用是通过对象名还是通过基类指针或引用去访问,如果是通过基类指针或引用去访问的,则应当声明为虚函数。(3) 有时,在定义虚函数时,并不定义其函数体,即函数体是空的。它的作用只是定义了一个虚函数名,具体功能留给派生类去添加。2021/3/936注意事项

28、: 使用虚函数,系统要有一定的空间开销。当一个类带有虚函数时,编译系统会为该类构造一个虚函数表(virtual function table,简称vtable),它是一个指针数组,存放每个虚函数的入口地址。系统在进行动态关联时的时间开销是很少的,因此,多态性是高效的。2021/3/937纯虚函数是一种特殊的虚函数,是一种没有具纯虚函数是一种特殊的虚函数,是一种没有具体实现的虚函数,其定义格式如下:体实现的虚函数,其定义格式如下:2021/3/938Class D1 also is an abstract class.例如:例如:下面举一个计算某些几何图形的面积下面举一个计算某些几何图形的面积之

29、和的例子,以说明纯虚函数及抽象类的之和的例子,以说明纯虚函数及抽象类的应用。例中各类之间的关系如下:应用。例中各类之间的关系如下:ShapeTriangleRectangleCircleTrapezoidSquareApplicationMyProgram2021/3/939# include Class ShapePublic: virtual double Area() const=0;class Circle: public Shape public: Circle(double r) R=r; double Area() const return 3.14*R*R; private:

30、double R;2021/3/940class Trapezoid: public Shape public: Trapezoid(double t, double b, double h) T=t; B=b; H=h; double Area() const return 0.5*(T+B)*H; private: double T,B,H;class Square: public Shape public: Square(double s) S=s; double Area() const return S*S; private: double S;2021/3/941class App

31、lication public: double Compute(Shape *s ,int n) const;double Application:Compute(Shape *s , int n) const double sum=0; for(int i=0; iArea(); return sum;2021/3/942class MyProgram : public Application MyProgram(); Myprogram(); double Run(); private: Shape *S;MyProgram:MyProgram() S=new Shape *5; S0=n

32、ew Triangle(3.0,5.0); S1=new Rectangle(5.0,8.0); S2=new Circle(8.5); S3=new Trapezoid(12.0,8.0,6.0); S4=new Square(6.8);2021/3/943MyProgram:MyProgram() for(int i=0; i5; i+) delete Si; /for(int i=0; i5; i+) delete S ;double MyProgram:Run() double sum=compute(S,5); return sum;void main() MyProgram M;

33、cout“Areas sum=”M.Run()endl; 输出结果:输出结果: Areas sum=380.7132021/3/9441、该程序定义了、该程序定义了8个类,其关系如前图所示;个类,其关系如前图所示;2、类、类Shape 定义为抽象类,相当于定义为抽象类,相当于Triangle 等等5个类的个类的根,根,Shape 类中定义纯虚函数类中定义纯虚函数Area(),在它的,在它的5个派生类个派生类中定义其具体实现;中定义其具体实现;3、在类、在类Application 中定义了计算面积和的函数中定义了计算面积和的函数Compute(),在其派生类,在其派生类MyProgram 中定义

34、中定义Run()函数,函数,以调用计算面积和函数以调用计算面积和函数Compute()。2021/3/945该例说明的设计特点:类库和应用程序设计分开。该例说明的设计特点:类库和应用程序设计分开。几何图形面积类库几何图形面积类库建立抽象类,作为继承的层次结构中的公建立抽象类,作为继承的层次结构中的公共根,方便图形的增添或减少。共根,方便图形的增添或减少。应用程序应用程序取决于用户需求,只需修改取决于用户需求,只需修改MyProgram 类的构造类的构造函数中的参数。函数中的参数。2021/3/946 抽象类包含一个或多个纯抽象类包含一个或多个纯virtual函数,这些函数必须要在具体函数,这些

35、函数必须要在具体的派生类派生。的派生类派生。 抽象类不能实例化对象。抽象类不能实例化对象。class character_device public: virtual int open(int opt)=0; virtual int close(int opt)=0; virtual int read(char *p, int n)=0; virtual int write(const char *p, int n)=0; virtual int ioct1(int )=0; virtual character_device() /virtual destructor;抽象类2021/3/947有时在基类中将某一成员函数定为虚函数,并不是基类本身的要求,而是考虑到

温馨提示

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

评论

0/150

提交评论