c++7 多态性[高教书苑]_第1页
c++7 多态性[高教书苑]_第2页
c++7 多态性[高教书苑]_第3页
c++7 多态性[高教书苑]_第4页
c++7 多态性[高教书苑]_第5页
已阅读5页,还剩48页未读 继续免费阅读

付费下载

下载本文档

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

文档简介

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

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

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

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

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

6、r *s) strcpy(name,s); void show_name() coutname“n”; ; 9高级教育 class 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= p-put_name(“Wang Ming”); /访问从基类继承的成员函数访问从基类继承的成员函数 Bobj.show_name(); Dobj.show_name(); dp

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

8、指针。根据类型适应 性的原则,一个指向基类的指针可用来指向以公有派生的任性的原则,一个指向基类的指针可用来指向以公有派生的任 何对象是何对象是C+ 实现运行时多态性的关键。实现运行时多态性的关键。 12高级教育 CommissionEmployee class CommissionEmployee public: CommissionEmployee( const string void setFirstName( const string / set first name string getFirstName() const; / return first name void setLas

9、tName( const string / set last name string getLastName() const; / return last name void setSocialSecurityNumber( const string / set SSN string getSocialSecurityNumber() const; / return SSN void setGrossSales( double ); / set gross sales amount double getGrossSales() const; / return gross sales amoun

10、t void setCommissionRate( double ); / set commission rate double getCommissionRate() const; / return commission rate double earnings() const; / calculate earnings void print() const; / print CommissionEmployee object private: string firstName; string lastName; string socialSecurityNumber; double gro

11、ssSales; / gross weekly sales double commissionRate; / commission percentage ; / end class CommissionEmployee 13高级教育 BasePlusCommissionEmployee class BasePlusCommissionEmployee : public CommissionEmployee public: BasePlusCommissionEmployee( const string void setBaseSalary( double ); / set base salar

12、y double getBaseSalary() const; / return base salary double earnings() const; / calculate earnings void print() const; / print BasePlusCommissionEmployee object private: double baseSalary; / base salary ; / end class BasePlusCommissionEmployee 14高级教育 派生类指针指向基类对象 int main() CommissionEmployee commiss

13、ionEmployee( Sue, Jones, 222-22-2222, 10000, .06 ); BasePlusCommissionEmployee *basePlusCommissionEmployeePtr = 0; / aim derived-class pointer at base-class object / Error: a CommissionEmployee is not a BasePlusCommissionEmployee basePlusCommissionEmployeePtr = return 0; / end main 15高级教育 基类对象指针指向派生

14、类对象 这种情况下,要访问派生类的成员函数, 要做: /强制类型转换 如: (BasePlusCommissionEmployee *) commissionEmployeePtr)-print(); 16高级教育 确定调用的具体对象的过程称为关联(binding)。 在这里是指把一个函数名与一个类对象捆绑在 一起,建立关联。一般地说,关联指把一个标 识符和一个存储地址联系起来。 7.2 静态关联与动态关联 17高级教育 在编译时即可确定其调用的函数,其过程称 为静态关联(static binding),由于是在运行前 进行关联的,故又称为早期关联(early binding)。 在运行阶段把

15、虚函数和类对象“绑定”在一 起的,此过程称为动态关联(dynamic binding)。 这种多态性是动态的多态性,即运行阶段的 多态性。由于动态关联是在编译以后的运行 阶段进行的,因此也称为滞后关联(late binding)。 18高级教育 多态性分为两类: 静态多态性和动态多态性。 (1)静态多态: (例7-1) (在程序编译时系统就能决定调用的是哪个函数), 因此静态多态性又称编译时的多态性。静态多 态是通过函数重载来实现的。 (2)动态多态(在例7-1基础上加virtual) 是在程序运行过程中才动态地确定操作所针对 的对象。它又称运行时的多态性。动态多态性 是通过虚函数(virtu

16、al function)实现的。 19高级教育 在类的继承层次结构中,在不同的层次中可 以出现名字相同、参数个数和类型都相同而 功能不同的函数。编译系统按照同名覆盖的 原则决定调用的对象。 例7.2 基类与派生类中有同名函数。 7.3 虚函数 7.3.1 虚函数的作用 20高级教育 虚函数的作用: 允许在派生类中重新定义与基类同名的 函数,并且通过基类指针或引用来访问基类 和派生类中的同名函数,实现动态多态的效 果。 21高级教育 虚函数是一种非静态的成员函数,定义格式如下:虚函数是一种非静态的成员函数,定义格式如下: virtual () / 其中,其中,virtual 是关键字。是关键字。

17、 如果某个类中的一个成员函数被说明为虚函数,该成员函如果某个类中的一个成员函数被说明为虚函数,该成员函 数可能在派生类中存在着不同的实现版本。数可能在派生类中存在着不同的实现版本。 由于存在有虚函数,编译器将进行动态联编,使调用虚函由于存在有虚函数,编译器将进行动态联编,使调用虚函 数的对象在运行时确定,以实现动态联编的多态性。数的对象在运行时确定,以实现动态联编的多态性。 22高级教育 例如:例如:使用虚函数将上例改为动态联编的情况,将得到不同的使用虚函数将上例改为动态联编的情况,将得到不同的 结果结果 #include class Base protected: int x; public

18、: Base(int a) x=a; virtual void print() cout“Base ”x“n”; ; 23高级教育 class First_d: public Base public: First_d(int a): Base(a) 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= p-pri

19、nt(); p= p-print(); obj2.print(); obj3.print(); 运行结果:运行结果: Base 1 First derivation 2 Second derivation 3 First derivation 2 Second derivation 3 程序中,程序中,p-print();出现了三出现了三 次,由于次,由于p 指向的对象不同,每次指向的对象不同,每次 执行了执行了print() 的不同实现版本。的不同实现版本。 25高级教育 1、在基类中,将该函数说明为虚函数、在基类中,将该函数说明为虚函数(virtual); 2、定义基类的公有派生类;、定义

20、基类的公有派生类; 3、在基类的公有派生类中重载该虚函数;、在基类的公有派生类中重载该虚函数; 4、定义指向基类的指针变量,它指向基类的公有派生类的对、定义指向基类的指针变量,它指向基类的公有派生类的对 象。象。 重载虚函数不是一般的重载函数,它要求函数名、返回重载虚函数不是一般的重载函数,它要求函数名、返回 类型、参数个数、参数类型和顺序完全相同。类型、参数个数、参数类型和顺序完全相同。 由于对虚函数进行重载,因此,由于对虚函数进行重载,因此,在派生类中的虚函数前在派生类中的虚函数前 的的virtual关键字可以省略。关键字可以省略。 26高级教育 例如:例如:下面是下面是“单界面、多实现版

21、本单界面、多实现版本”概念的另一个程序例子概念的另一个程序例子 #include class figure protected: double x,y; public: void set_dim(double i; double j=0) x=i; y=j; virtual void show_area() cout“No area computation define ”; cout“for this class.n”; ; 27高级教育 class triangle: public figure public: void show_area() cout“Triangle with hig

22、h”; coutx“and base”y; cout“has an ares of”; coutx*0.5*y; ; class square: public figure public: void show_area() 28高级教育 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

23、”; coutset_dim(10.0,5.0); p-show_area(); Triangle with high 10 and 5 has an area of 25.0 Square with dimension 10*5 has an area of 50.0 Circle with radius 9 has an area of 254.34 p= p-show_area(); p= p-set_dim(9.0); p-show_area(); 30高级教育 虚函数的使用方法: (1) 在基类用virtual声明成员函数为虚函数。这样 就可以在派生类中重新定义此函数,为它赋予 新的

24、功能,并能方便地被调用。 注意事项: 在派生类中重新定义此函数,要求函 数名、函数类型、函数参数个数和类型全部与 基类的虚函数相同,并根据派生类的需要重新 定义函数体。 31高级教育 (2) 定义一个指向基类对象的指针变量或对象 的引用,并使它指向同一类族中需要调用该 函数的对象。(即使用赋值兼容规则) (3) 通过该指针变量或对象的引用调用此虚函 数。 示例:指针调用,引用调用 32高级教育 使用虚函数时,有几点要注意: (1)(1)只能用只能用virtualvirtual声明类的成员函数,使它成为虚声明类的成员函数,使它成为虚 函数函数,而不能将类外的普通函数声明为虚函数。因,而不能将类外

25、的普通函数声明为虚函数。因 为虚函数的作用是允许在派生类中对基类的虚函数为虚函数的作用是允许在派生类中对基类的虚函数 重新定义。重新定义。显然,它只能用于类的继承层次结构中显然,它只能用于类的继承层次结构中。 (2) (2) 一个成员函数被声明为虚函数后,在同一类族一个成员函数被声明为虚函数后,在同一类族 中的类就不能再定义一个中的类就不能再定义一个非非virtualvirtual的但与该虚函的但与该虚函 数具有相同的参数数具有相同的参数( (包括个数和类型包括个数和类型) )和函数返回值和函数返回值 类型的同名函数。类型的同名函数。 (3)(3)构造函数不能声明为虚函数构造函数不能声明为虚函

26、数。这是因为在执行。这是因为在执行 构造函数时类对象还未完成建立过程,当然谈不上构造函数时类对象还未完成建立过程,当然谈不上 函数与类对象的绑定。函数与类对象的绑定。 33高级教育 (1) 首先看成员函数所在的类是否会作为基类。 然后看成员函数在类的继承后有无可能被更 改功能,如果希望更改其功能的,一般应该 将它声明为虚函数。 如果成员函数在类被继承后功能不需修改, 或派生类用不到该函数,则不要把它声明为 虚函数。不要仅仅考虑到要作为基类而把类 中的所有成员函数都声明为虚函数。 7.3.2 在什么情况下应当声明虚函数 34高级教育 (2) 应考虑对成员函数的调用是通过对象名还 是通过基类指针或

27、引用去访问,如果是通过 基类指针或引用去访问的,则应当声明为虚 函数。 (3) 有时,在定义虚函数时,并不定义其函数 体,即函数体是空的。它的作用只是定义了 一个虚函数名,具体功能留给派生类去添加。 35高级教育 注意事项: 使用虚函数,系统要有一定的空间开销。 当一个类带有虚函数时,编译系统会为该类 构造一个虚函数表(virtual function table,简 称vtable),它是一个指针数组,存放每个虚 函数的入口地址。系统在进行动态关联时的 时间开销是很少的,因此,多态性是高效的。 36高级教育 纯虚函数是一种特殊的虚函数,是一种没有具纯虚函数是一种特殊的虚函数,是一种没有具 体

28、实现的虚函数,其定义格式如下:体实现的虚函数,其定义格式如下: 37高级教育 Class D1 also is an abstract class. 例如:例如:下面举一个计算某些几何图形的面积下面举一个计算某些几何图形的面积 之和的例子,以说明纯虚函数及抽象类的之和的例子,以说明纯虚函数及抽象类的 应用。例中各类之间的关系如下:应用。例中各类之间的关系如下: Shape TriangleRectangleCircleTrapezoidSquare Application MyProgram 38高级教育 # include Class Shape Public: virtual double

29、 Area() const=0; ; class Circle: public Shape public: Circle(double r) R=r; double Area() const return 3.14*R*R; private: double R; ; 39高级教育 class 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; ; c

30、lass Square: public Shape public: Square(double s) S=s; double Area() const return S*S; private: double S; ; 40高级教育 class Application 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; 41高级教育 class MyProg

31、ram : public Application MyProgram(); Myprogram(); double Run(); private: Shape *S; ; MyProgram:MyProgram() S=new Shape *5; S0=new 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); 42高级教育 MyProgram:MyProgram() for(int i=0; i5; i+) d

32、elete Si; /for(int i=0; i5; i+) delete S ; double MyProgram:Run() double sum=compute(S,5); return sum; void main() MyProgram M; cout“Areas sum=”M.Run()endl; 输出结果:输出结果: Areas sum=380.713 43高级教育 1、该程序定义了、该程序定义了8个类,其关系如前图所示;个类,其关系如前图所示; 2、类、类Shape 定义为抽象类,相当于定义为抽象类,相当于Triangle 等等5个类的个类的 根,根,Shape 类中定义纯虚

33、函数类中定义纯虚函数Area(),在它的,在它的5个派生类个派生类 中定义其具体实现;中定义其具体实现; 3、在类、在类Application 中定义了计算面积和的函数中定义了计算面积和的函数 Compute(),在其派生类,在其派生类MyProgram 中定义中定义Run()函数,函数, 以调用计算面积和函数以调用计算面积和函数Compute()。 44高级教育 该例说明的设计特点:类库和应用程序设计分开。该例说明的设计特点:类库和应用程序设计分开。 几何图形面积类库几何图形面积类库建立抽象类,作为继承的层次结构中的公建立抽象类,作为继承的层次结构中的公 共根,方便图形的增添或减少。共根,方

34、便图形的增添或减少。 应用程序应用程序取决于用户需求,只需修改取决于用户需求,只需修改MyProgram 类的构造类的构造 函数中的参数。函数中的参数。 45高级教育 抽象类包含一个或多个纯抽象类包含一个或多个纯virtual函数,这些函数必须要在具体函数,这些函数必须要在具体 的派生类派生。的派生类派生。 抽象类不能实例化对象。抽象类不能实例化对象。 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 ; 抽象类 46高级教育 有时在基类中将某一成员函数定为虚函数, 并不是基类本身的要求,而是考虑到派生类 的需要,在基类中预留了一

温馨提示

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

评论

0/150

提交评论