版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、 多态性是面向对象程序设计语言的又一重要特征,它是指不同多态性是面向对象程序设计语言的又一重要特征,它是指不同对象接收到同一消息时会产生不同的行为。对象接收到同一消息时会产生不同的行为。 继承所处理的是类与类之间的层次关系问题,而多态则是处理继承所处理的是类与类之间的层次关系问题,而多态则是处理类的层次结构之间,以及同一个类内部同名函数的关系问题。类的层次结构之间,以及同一个类内部同名函数的关系问题。 简单地说,多态就是在同一个类或继承体系结构的基类与派生简单地说,多态就是在同一个类或继承体系结构的基类与派生类中,用同名函数来实现各种不同的功能。类中,用同名函数来实现各种不同的功能。 多态是面
2、向对象理论的三大支柱之一。另外两个支柱是封装和多态是面向对象理论的三大支柱之一。另外两个支柱是封装和继承。继承。1. 多态多态(polymorphism) 对象根据所接收的消息而做出动作,同样的消息为不同的对象对象根据所接收的消息而做出动作,同样的消息为不同的对象接收时可导致完全不同的行动,该现象称为多态性。接收时可导致完全不同的行动,该现象称为多态性。6.1 多态的概念多态的概念 通俗地讲,就是用一个相同的名字定义许多不同的函数,这些通俗地讲,就是用一个相同的名字定义许多不同的函数,这些函数可以针对不同数据类型实现相同或相似的功能,即一个接口,函数可以针对不同数据类型实现相同或相似的功能,即
3、一个接口,多种实现。多种实现。2. 联编联编 C+中的多态性与联编这一概念密切相关。一个程序常常会调中的多态性与联编这一概念密切相关。一个程序常常会调用到来自于不同文件或用到来自于不同文件或C+库中的资源库中的资源(如函数、对话框如函数、对话框)等,需等,需要经过编译、连接才能形成可执行文件,在这个过程中要把调用要经过编译、连接才能形成可执行文件,在这个过程中要把调用函数名与对应函数函数名与对应函数(这些函数可能来源于不同的文件或库这些函数可能来源于不同的文件或库)关联在关联在一起,这个过程称为绑定一起,这个过程称为绑定(binding),即联编。,即联编。3. 静态联编与动态联编静态联编与动
4、态联编(1)静态联编静态联编 在编译程序时根据调用函数提供的信息,把它所对应的具体函在编译程序时根据调用函数提供的信息,把它所对应的具体函数确定下来,即在编译时把调用函数名与具体函数绑定在一起。数确定下来,即在编译时把调用函数名与具体函数绑定在一起。(2)动态联编动态联编 在编译程序时还不能确定函数调用所对应的具体函数,只有在在编译程序时还不能确定函数调用所对应的具体函数,只有在程序运行过程中才能确定函数调用所对应的具体函数,即在程序程序运行过程中才能确定函数调用所对应的具体函数,即在程序运行时才把调用函数名与具体函数绑定在一起。运行时才把调用函数名与具体函数绑定在一起。4. 多态性的实现多态
5、性的实现(1)编译时多态性编译时多态性-静态联编静态联编(连接连接) 系统在编译时就决定如何实现某一动作,即对某一消息如何处系统在编译时就决定如何实现某一动作,即对某一消息如何处理。静态联编具有执行速度快的优点。在理。静态联编具有执行速度快的优点。在C+中的编译时多态性中的编译时多态性是通过函数重载和运算符重载实现的。是通过函数重载和运算符重载实现的。(2)运行时多态性运行时多态性-动态联编动态联编(连接连接) 系统在运行时动态实现某一动作,即对某一消息在运行过程系统在运行时动态实现某一动作,即对某一消息在运行过程中才确定其如何响应。动态联编为系统提供了灵活和高度问题抽中才确定其如何响应。动态
6、联编为系统提供了灵活和高度问题抽象的优点。在象的优点。在C+中的运行时多态性是通过继承和虚函数实现的。中的运行时多态性是通过继承和虚函数实现的。例如:求图形的面积例如:求图形的面积类:类:shape操作:求面积操作:求面积类:圆类:圆操作:求面积操作:求面积类:长方形类:长方形操作:求面积操作:求面积解决的方法:用解决的方法:用静态联编和动态绑定实现静态联编和动态绑定实现例例6.1.1 函数重载具有编译时多态性函数重载具有编译时多态性#include class A public: void print( ) cout A; ; class B:public A /定义派生类定义派生类 pub
7、lic: void print( ) cout B; ; /函数重新定义函数重新定义void main( ) A a; B b; a.print( ); /访问基类函数访问基类函数 b.print( ); /访问派生类函数,实现了多态访问派生类函数,实现了多态 运行结果:AB6.2 虚函数虚函数1.虚函数的意义虚函数的意义(1)回顾:基类与派生类对象的赋值相容回顾:基类与派生类对象的赋值相容 第一、派生类对象可以赋值给基类对象。第一、派生类对象可以赋值给基类对象。 第二、派生类对象的地址可以赋值给指向基类的指针。第二、派生类对象的地址可以赋值给指向基类的指针。 第三、派生类对象可以作为基类对象
8、的引用。第三、派生类对象可以作为基类对象的引用。(2)赋值相容问题赋值相容问题 不论采用哪种赋值方式,都只能通过基类对象不论采用哪种赋值方式,都只能通过基类对象(或基类或基类对象的指针或引用对象的指针或引用)访问到派生类对象从基类中继承到的访问到派生类对象从基类中继承到的成员,不能借此访问派生类定义的成员。成员,不能借此访问派生类定义的成员。(3)虚函数的意义虚函数的意义 使得通过基类对象的指针或引用访问派生类定义的成使得通过基类对象的指针或引用访问派生类定义的成员可以实施。员可以实施。例6.3 多态性在人事管理系统中的应用/Save as employee.h#ifndef EMPLOYEE
9、_H#define EMPLOYEE_Hclass Employee/雇员,基类Employeepublic:Employee(const char *,const char *);virtual Employee();/析构函数为虚函数char *getName() const;/读取姓名char *getID() const;/读取身份证号virtual float getSalary() const;/虚函数,读取工资virtual void print() const;/虚函数,显示 private:char *Name;/姓名char *ID; ; /身份证号#endif/Save
10、as employee.cpp#include #include #include employee.hEmployee:Employee(const char *pn,const char *pid)Name=new char strlen(pn)+1;strcpy(Name,pn);ID=new char strlen(pid)+1;strcpy(ID,pid);Employee:Employee()delete Name;delete ID; char *Employee:getName() constreturn Name;/返回指向姓名的指针char *Employee:getID(
11、) constreturn ID; /返回指向身份证号的指针float Employee:getSalary() constreturn 0.0;/虚函数,形式上的返回,真正的返回值由其派生类实现void Employee:print() constcoutAn employee! endl;/虚函数,形式上的输出/Save as manager.h#ifndef MANAGRE_H#define MANAGER_H#include Employee.hclass Manager:public Employee/经理,公有继承public:Manager(const char *,const
12、char *,float =0.0);/姓名,身份证号,工资virtual Manager();void setSalary(float);/设置工资virtual float getSalary() const;/虚函数,读取工资virtual void print() const;/虚函数,显示private:float weeklySalary;/经理的周工资;#endif/Save as manager.cpp#include #include employee.h#include manager.hManager:Manager(const char *pn,const char *
13、pid,float s):Employee(pn,pid)/初始化表调用基类构造函数初始化setSalary(s);/设置工资Manager:Manager() ;/自动执行基类析构函数void Manager:setSalary(float s)weeklySalary=s;/设置工资float Manager:getSalary() constreturn weeklySalary;/真正的返回值void Manager:print() const/经理的输出coutManager: getName()/派生类内部访问基类成员 ; ID: getID()endl; /派生类内部访问基类成员
14、/Save as salesman.h#ifndef SALESMAN_H#define SALESMAN_H#include employee.hclass Salesman:public Employee/销售人员,公有继承public:Salesman(const char *,const char *,float =0.0,float =0.0,unsigned =0);virtual Salesman();void setSalary(float);/周基本工资赋值virtual float getSalary() const;/虚函数,读取工资virtual void print(
15、) const;/虚函数,显示private:float baseSalary;/周基本工资float commission;/每件销售佣金unsigned quanlity;/周销售数;#endif/Save as salesman.cpp#include #include salesman.hSalesman:Salesman(const char *pn,const char *pid,float bs,float c,unsigned q):Employee(pn,pid)/初始化表调用基类构造函数baseSalary=bs;commission=c;quanlity=q;Salesm
16、an:Salesman() ; /自动执行基类析构函数void Salesman:setSalary(float bs)baseSalary=bs;/周基本工资赋值float Salesman:getSalary() constreturn (baseSalary+commission*quanlity);/真正的返回void Salesman:print() const/销售员输出coutSalesman: getName()/派生类内部访问基类成员; ID: getID()endl;/派生类内部访问基类成员/Save as main.cpp#include #include manager
17、.h#include salesman.hvoid main()Employee *ptr;/定义Employee类的指针Manager m(Zhang Hua,320103820920201,1500.0);Salesman sm(Yang Hong,310105770411202,300.0,9.5,95);ptr=&m; /ptr指向经理,动态绑定ptr-print();/print()为虚函数,输出经理信息而不是An employee! coutEarnings per week: getSalary()endl;/通过指针实现多态coutEarnings per week:
18、m.getSalary()endl;/通过对象变量实现coutprint(); /print()为虚函数,输出销售员信息,而不是An employeecoutEarnings per week: getSalary()endl;/动态联编coutEarnings per week: sm.getSalary()endl;/静态联编运行结果:运行结果:说明:说明:(1)可只在基类的成员函数前加可只在基类的成员函数前加 virtual, 在派生类中不加在派生类中不加, 但为了便于阅读但为了便于阅读, 可在所有要声明为虚函数的前面都加可在所有要声明为虚函数的前面都加 virtual。(2)只需要在类
19、定义文件只需要在类定义文件, 即头文件中的虚函数前加即头文件中的虚函数前加virtual, 在在cpp文件中就不要再加文件中就不要再加virtual。(3)通过指向派生类对象的基类指针访问成员函数时,非通过指向派生类对象的基类指针访问成员函数时,非虚函数由指针类型决定调用的函数版本;虚函数由指针虚函数由指针类型决定调用的函数版本;虚函数由指针指向的实际对象决定调用的函数版本。指向的实际对象决定调用的函数版本。4.虚函数的特性虚函数的特性(1)一旦将某个成员函数声明为虚函数后,它在继承体系一旦将某个成员函数声明为虚函数后,它在继承体系中将永远为虚函数。中将永远为虚函数。(2)如果基类定义了虚函数
20、,当通过基类指针或引用调用如果基类定义了虚函数,当通过基类指针或引用调用派生类对象时,将访问到它们实际所指对象中的虚函数派生类对象时,将访问到它们实际所指对象中的虚函数版本。版本。(3)只有通过基类对象的指针和引用访问派生类对象的虚只有通过基类对象的指针和引用访问派生类对象的虚函数时,才能体现动态多态性。函数时,才能体现动态多态性。(4)派生类中的虚函数要保持其虚特征,必须与基类虚函派生类中的虚函数要保持其虚特征,必须与基类虚函数的函数原型完全相同,否则就是普通的重载函数,与数的函数原型完全相同,否则就是普通的重载函数,与基类的虚函数无关。基类的虚函数无关。例例6.4 基类基类base和派生类
21、和派生类derive都有成员函数都有成员函数f2(),但它们的参,但它们的参数不同,因此不能体现成员函数数不同,因此不能体现成员函数f2()在派生类在派生类derive中的虚函数中的虚函数特性。特性。#include class base public: virtual void f1( ) cout f1 function of base n ; /定义虚函数定义虚函数 virtual void f2( ) cout f2 function of base n ; /定义虚函数定义虚函数 void f3( ) cout f3 function of base n ; /定义一般函数定义一般函
22、数 ; class derive: public base public: void f1( ) cout f1 function of derive n ; /重新定义虚函数重新定义虚函数 void f2(int x ) cout f2 function of derive n ; /重新定义虚函数,重新定义虚函数, /由于形式改变,失去虚函数功能由于形式改变,失去虚函数功能 void f3( ) cout f1( ); /调基类函数调基类函数 ptr-f2( ); /调基类函数调基类函数 ptr-f3( ); /调基类函数调基类函数 rD.f1( ); /调派生类函数调派生类函数,多态性多
23、态性 rD.f2( ); /调基类函数调基类函数,由于由于f2的形式改变,不具多态性的形式改变,不具多态性 rD.f3( ); /调基类函数调基类函数,由于没有声明为虚函数,不具多态性由于没有声明为虚函数,不具多态性结果结果: f1 function of base f2 function of base f3 function of base f1 function of derive f2 function of base f3 function of base(5)派生类对象通过从基类继承的成员函数调用虚函数时,将访派生类对象通过从基类继承的成员函数调用虚函数时,将访问到派生类中的版本。
24、如:派生类问到派生类中的版本。如:派生类D的对象通过继承到的基类的对象通过继承到的基类B的成员函数的成员函数f()将访问到派生类将访问到派生类D中的虚函数中的虚函数g()。#include class Bpublic: void f() g(); virtual void g() cout B:g; ;class D : public Bpublic: void g() cout D:g; ;void main() D d; d.f(); 运行结果运行结果: D:g(6)只有类的非静态成员函数才能被定义为虚函数,类的构造函只有类的非静态成员函数才能被定义为虚函数,类的构造函数和静态成员函数不能
25、定义为虚函数。因为虚函数在继承层次数和静态成员函数不能定义为虚函数。因为虚函数在继承层次结构中才能够发生作用,而构造函数、静态成员是不能够被继结构中才能够发生作用,而构造函数、静态成员是不能够被继承的。承的。(7)内联函数也不能定义为虚函数。因为内联函数采用的是静态内联函数也不能定义为虚函数。因为内联函数采用的是静态联编的方式,而虚函数是在程序运行时才与具体函数动态绑定联编的方式,而虚函数是在程序运行时才与具体函数动态绑定的,采用的是动态联编的方式,即使虚函数在类体内被定义,的,采用的是动态联编的方式,即使虚函数在类体内被定义,C+编译器也将它视为非内联函数。编译器也将它视为非内联函数。5.虚
26、析构函数虚析构函数 基类析构函数几乎总是为虚析构函数。假定使用基类析构函数几乎总是为虚析构函数。假定使用delete和一和一个指向派生类的基类指针来销毁派生类对象,如果基类析构函个指向派生类的基类指针来销毁派生类对象,如果基类析构函数不为虚数不为虚, 是一个普通成员函数,则在通过基类对象的引用或是一个普通成员函数,则在通过基类对象的引用或指针对派生类对象析构时将调用到基类析构函数,致使派生类指针对派生类对象析构时将调用到基类析构函数,致使派生类对象析构不彻底!对象析构不彻底!例:在非虚析构函数的情况下,通过基类指针对派生对象的析例:在非虚析构函数的情况下,通过基类指针对派生对象的析构不彻底。构
27、不彻底。#include class Apublic: A() coutcall A:A()endl; ;/析构函数非虚析构函数非虚class B:public A char *buf;public: B(int i)buf=new chari; B() delete buf; coutcall B:B()endl; ;void main() A* a=new B(10); delete a; 运行结果运行结果: call A:A()此结果表明没有此结果表明没有析构析构buf。例:在虚析构函数的情况下,通过基类指针对派生对象的析构例:在虚析构函数的情况下,通过基类指针对派生对象的析构是彻底的。
28、是彻底的。#include class Apublic: virtual A() cout“call A:A()”endl; ;/析构函数为虚析构函数为虚class B:public A char *buf;public: B(int i)buf=new chari; B() delete buf; coutcall B:B()endl; ;void main() A* a=new B(10); delete a; 运行结果运行结果: call B:B()call A:A()此结果表明析构此结果表明析构了了buf。6.多重继承中的多态性多重继承中的多态性 对于多重派生类,其多态性又将如何传递呢
29、?请看下面例对于多重派生类,其多态性又将如何传递呢?请看下面例子。子。base1, 有虚函数有虚函数bdderive,有多态性有多态性bderive,有多态性有多态性bbderive:base1对象调用对象调用: 有多态性有多态性base2对象调用对象调用: 无多态性无多态性base2,无虚函数无虚函数例例6.5 多继承关系中的多态性多继承关系中的多态性#includeclass base1 /定义基类定义基类 base1public: virtual void f( ) cout base1 n; ; /定义虚函数定义虚函数class base2 /定义基类定义基类 base2 public
30、: void f( ) cout base2 n ; ; /定义一般函数定义一般函数class bderive: public base1 /定义定义base1 的派生类的派生类public: virtual void f( ) cout bderive n ; ; /重新定义虚函数重新定义虚函数class bdderive: public bderive /定义派生类定义派生类bderive的派生类的派生类 bdderivepublic: virtual void f( ) cout bdderive n ; ; /重新定义虚函数重新定义虚函数class bbderive: public b
31、ase1, public base2 /定义基类定义基类 base1 和和 base2的派生类的派生类 public: virtual void f( ) cout f( ); /调调base1基类函数基类函数 p1=&bbd; p1-f( ); /调派生类函数调派生类函数,多态性多态性 p2=&bbd; p2-f( ); /调调base2基类函数基类函数,无多态性无多态性 p1=&bdd; p1-f( ); /调派生类函数调派生类函数,有多态性有多态性 运行结果运行结果: base1 bbderive base2 bddrive说明:说明: 在多重继承中,若某一基类无
32、虚函数定义,则由在多重继承中,若某一基类无虚函数定义,则由该基类派生类的对象不具有多态性。该基类派生类的对象不具有多态性。 base2 *p2; /基类基类 base2中没有定义中没有定义 f( )为虚函数为虚函数 p2=&bbd; p2-f( ); /调基类函数调基类函数, 无多态性无多态性6.3 纯虚函数和抽象类纯虚函数和抽象类1.纯虚函数纯虚函数 在在C中,仅为多态机制提供接口中,仅为多态机制提供接口(占用一个位置占用一个位置)而没有而没有任何实体定义的函数。任何实体定义的函数。 纯虚函数只给出函数声明,不给出具体实现内容,同时在纯虚函数只给出函数声明,不给出具体实现内容,同时在
33、声明函数原型之后须赋声明函数原型之后须赋“0”值。其一般形式为:值。其一般形式为:如:如:class X virtual type functionname(parameters) = 0; ; 纯虚函数一般在基类中定义,在派生类中重载。纯虚函数一般在基类中定义,在派生类中重载。2.抽象类抽象类 其中至少包含了一个纯虚函数的类称为抽象类。且规定:其中至少包含了一个纯虚函数的类称为抽象类。且规定:(1)由于纯虚函数没有实现代码,抽象类不能生成对象。由于纯虚函数没有实现代码,抽象类不能生成对象。(2)抽象类不能作为参数的类型。抽象类不能作为参数的类型。(3)抽象类仅能作为基类,从而可声明指针或引用
34、抽象类仅能作为基类,从而可声明指针或引用, 通过抽象通过抽象类的指针或引用访问到它的派生类的成员函数,实现运行时类的指针或引用访问到它的派生类的成员函数,实现运行时的多态性。的多态性。(4)如果派生类只是简单地继承了抽象类的纯虚函数,而没有如果派生类只是简单地继承了抽象类的纯虚函数,而没有重新定义基类的纯虚函数,则派生类也是一个抽象类。重新定义基类的纯虚函数,则派生类也是一个抽象类。例例6.6 多态性的应用多态性的应用: 计算图形面积,其中计算图形面积,其中shape为一般有关图为一般有关图形的特征描述,是基类。类形的特征描述,是基类。类circle代表代表“圆圆”, square代表代表“正方形正方形”。如下图所示。如下图所示。shape:虚函数:虚函数area( )square:area( ): 多态多态circle: area( ): 多态多态/Save as shape.h#ifndef SHAPE_H#define SHAPE_Hclass shapepublic:virtual float area() const =0; /声明纯虚函数声明纯虚函数virtual void print(
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- LY/T 1180-2025脲醛预缩液
- 【案例】iEMS助推工厂减碳管理实现企业节能降耗
- 员工自愿出院协议书
- 业主保安恋爱协议书
- 维语高考考试题目及答案
- 2026年颞叶癫痫规范化诊疗试题及答案(神经内科版)
- 通化市辅警招聘考试题及答案
- 云计算阿里云ECS配置题库及答案
- 医学影像CT题库及答案
- 针灸推拿试题及答案
- 2025年中国中医药出版社招聘笔试参考题库含答案解析
- 2025中级消防设施操作员作业考试题及答案(1000题)
- 申请建房报告范文
- 高速铁路供电安全检测监测系统(6C系统)总体技术规范
- 人社部发布:职称评审监管暂行办法全解读
- L6562PFC设计参数自动计算电子表格表格
- 药品生产管理-《药品生产质量管理规范》(药事管理课件)
- 财政与税收第七版微课版王晓光课后参考答案
- 钢结构工程投标方案(技术方案)
- 《认识人民币》教学课件(人教版小学数学一年级下册)
- T-CI 284-2024 手卡指压式星状神经节埋线技术操作规范
评论
0/150
提交评论