c++语言简明教程 教学课件 ppt 作者 吴延海_ 第5章

上传人:E**** 文档编号:89343103 上传时间:2019-05-23 格式:PPT 页数:56 大小:257.50KB
返回 下载 相关 举报
c++语言简明教程 教学课件 ppt 作者 吴延海_ 第5章_第1页
第1页 / 共56页
c++语言简明教程 教学课件 ppt 作者 吴延海_ 第5章_第2页
第2页 / 共56页
c++语言简明教程 教学课件 ppt 作者 吴延海_ 第5章_第3页
第3页 / 共56页
c++语言简明教程 教学课件 ppt 作者 吴延海_ 第5章_第4页
第4页 / 共56页
c++语言简明教程 教学课件 ppt 作者 吴延海_ 第5章_第5页
第5页 / 共56页
点击查看更多>>
资源描述

《c++语言简明教程 教学课件 ppt 作者 吴延海_ 第5章》由会员分享,可在线阅读,更多相关《c++语言简明教程 教学课件 ppt 作者 吴延海_ 第5章(56页珍藏版)》请在金锄头文库上搜索。

1、,第5章 类的多态性,5.1 类的多态 5.2 运行时的多态性 本章小结,多态性是面向对象程序设计的一个重要特征,多态性有很多种,反映出客观世界的多样性,能够提供程序设计的极大灵活性。,多态性的种类很多,运行时的多态性是多态性中重要的一种,C+ 语言用虚函数方法实现运行时的多态性。,5.1 类 的 多 态,5.1.1 多态性的概念 多态性是面向对象程序设计的一个重要特征。“一个函数名,不同的功能”,这是多态性最简单的表述。类的多态性体现在面向对象程序设计的许多方面,函数重载、函数的覆盖继承、虚函数以及类模板均是多态性的体现。,函数重载要求重载的函数在同一名字空间中使用相同的函数名,但必须保证函

2、数的参数个数或者参数类型不同,编译环境利用名字分裂机制在内部区分了各个重载的函数,认为是不同的函数。这样同一个对象调用相同的成员函数时,会由于参数个数不同或参数类型不同而产生不同的调用结果。 前面讲过,在类继承中派生类可以覆盖基类定义过的成员函数,要求派生类中要覆盖的成员函数在其函数名、参数个数和参数类型与基类成员函数完全一样。,否则,系统将按成员函数重载考虑。当派生类覆盖了基类的某个成员函数时,如果派生类对象调用了覆盖的成员函数时,则系统匹配的方法是自当前派生类层向上逐层匹配,直到匹配到一个成员函数为止。 但是,在程序设计时,仅仅通过派生类是否覆盖基类定义过的成员函数提供的多态性有时不够灵活

3、。比如考虑到赋值兼容准则,一个基类的对象指针可能指向派生类,那么该对象指针调用了覆盖函数的话,系统只是调用基类中的该函数,而并未调用派生类中的函数。,如果对于一个类层次来说,同样一个成员函数的调用,程序中动态确定的对象不同,则调用的成员函数不同。这样多态性能提供程序设计更大的灵活性,此多态性称做运行时的多态性。本章主要讨论这种多态性。 另外,面向对象技术还支持函数参数的多态性,这需要使用模板方法来实现。,5.1.2 多态性的种类 如上节所述,面向对象技术支持的多态性主要包括四种:强制多态、重载多态、包含多态和参数类型多态。 (1) 强制多态是指将一种类型的值转换为另一种类型的值进行的语义操作,

4、从而防止类型错误。类型转换可以是隐式的,在编译时完成;也可以是显式的,可在动态运行时完成。C+ 语言定义了基本数据类型之间的隐式转换规则,即,char-short-int-unsigned-long-unsigned long-float-double-long double 但在赋值运算中,这一规则并不适用。赋值运算时,是将赋值运算符右边变量的数据类型转换为运算符左边变量的数据类型。前面讲过,C+ 语言可使用两种语法进行强制类型转换: (类型说明符)变量名 或 类型说明符(变量名) 另一种重要的显式类型转换就是构造函数,构造函数进行的类型转换只能将参数类型向类类型转换。如果要把类类型的数据转

5、换成所指定的某种数据类型,就需要使用类型转换函数。,类型转换函数又称为类型强制转换成员函数,它是类中的一个非静态成员函数。它的定义格式如下: class public: operator ( ); ; 这个转换函数定义了由到之间的映射关系。,【例5.1】 类型转换函数范例:将分数类转换为双精度型。 /- /c0501.cpp /- #include class Rational private: int den; /分数的分子,int num; /分数的分母 public: operator double(); /类型强制转换成员函数的声明 Rational(int d, int n) /构造

6、函数 den=d; num=n; ;,Rational:operator double( ) /类型强制转换成员函数的定义 return double(den)/double(num); /返回double型的分数值 void main() Rational r(5,8); double d=4.7; d+=r; /隐式调用类型强制转换成员函数,coutdendl; 执行该程序输出结果:5.325。 说明: 程序中d是一个double型数据,r是Rational类对象。这两个不同类型的数据的加法之所以能够进行,是得益于类型转换函数operator double( )。当然也可以在程序中使用显式

7、类型强制转换函数,将加法语句改为d+=double(r)。 定义类型转换函数时需要注意的是:类型转换函数不可以有返回值,类型转换函数的名称就是类型转换的目标类型。,类型转换函数也不带任何参数,它用于将本类型的数值或变量转换成其他类型;也不可以将类型转换函数定义为友元函数。 (2) 重载多态是多态性中最简单的形式,它分为函数重载和运算符重载。函数重载允许功能相近的函数使用相同的函数名,编译系统依据这些函数参数个数或者参数类型不同区别不同的函数。运算符重载是对已有运算符赋予多重含义,同一个运算符作用于不同类型的数据导致不同类型的行为。它的实质是函数重载,重载的是已有的运算符,在下一章中我们将重点介

8、绍。,(3) 包含多态是通过虚函数来实现的。如果派生类中覆盖了基类对象的函数,根据赋值兼容,用基类类型的指针指向派生类,就可以通过这个指针来使用派生类的成员函数。如果这个函数是普通的成员函数,通过基类类型的指针访问到的只能是基类的同名成员。而如果将它设置为虚函数,则可以使用基类类型的指针访问到指针正在指向的派生类的同名函数。这样,通过基类类型的指针,就可以使属于不同派生类的不同对象产生不同的行为,从而实现运行过程的多态。,(4) 参数多态与类模板相关联,它把功能相似仅数据类型(或类类型)不同的函数或类设计为通用的函数模板或类模板,从而实现了相同的函数或类的成员函数对多种数据类型的数据处理。 C

9、+ 语言中,函数重载和子类对父类成员函数的覆盖继承,实现的都是编译时的多态性,而虚函数实现的是运行时的多态性。所谓编译时的多态性就是指系统在编译时就确定了调用同名函数中的具体哪一个函数,而运行时的多态性则需要系统在运行期间根据每一个对象指针指向的对象确定调用父类或是子类的成员函数,虚函数标示了采用滞后联编实现运行时的多态性。,C+语言是利用虚函数实现运行时的多态性的。对于存在虚函数的类层次中,系统将按照滞后联编的方式考虑调用对象的成员函数。,5.2 运行时的多态性,5.2.1 滞后联编 联编(binding)就是将一个标示符名和一个内存地址绑定在一起的过程。在这个过程中将对每个函数调用分配内存

10、地址,并且对外部访问也分配地址,这样就将标示符名和存储地址结合在一起。根据联编所处的阶段不同,可以分为两种不同的联编方式:早期联编和滞后联编(也称静态联编和动态联编)。对于同名函数的调用,如果在编译时决定执行哪个同名函数,则称为早期联编,,反之在编译阶段不能决定执行哪个同名的被调用函数,只在执行阶段才能依据要处理的对象的类型来决定执行哪个类的成员函数,这称为滞后联编。一些支持面向对象的程序设计语言(如Java语言)完全使用滞后联编,而C+ 语言兼容C语言,采用了两种联编方式结合的编译方式。C+ 中用虚函数来标示滞后联编,即对于没有标记为虚函数的成员函数调用,系统采用早期联编;对于标示为虚函数的

11、则采用滞后联编。,由于存在函数重载,以及存在派生类对基类成员函数的继承和覆盖,因此要确定每个对象消息(即成员函数的调用)和类层次中某类的成员函数的匹配。这既可以采用早期联编的方式确定,也可以采用滞后联编的方式确定。早期联编在编译阶段必须了解所有的函数和模块执行所需检测的信息,它对函数的选择是基于指向对象的指针(或引用)的类型。而滞后联编对成员函数的选择不是基于指针或引用,而是基于对象类型,针对不同的对象类型将做出不同的编译结果。,5.2.2 虚函数 C+ 语言中,使用虚函数标示滞后联编,在类的成员函数前加virtual关键字就构成虚函数。声明虚函数的语句格式为 virtual 只有在类层次中才

12、可以声明虚函数,虚函数存在于类继承中,只有一个类中存在虚函数是无意义的。和函数的覆盖继承一样,派生类中虚函数的函数名、参数个数、参数类型以及返回类型必须与基类中声明的虚函数完全一样,并且要求派生类必须公有继承基类。,要使用虚函数时,在外部程序中定义一个指向基类对象的指针。依据赋值兼容准则,该指针也可指向派生类对象。如果该对象指针调用类层次中的虚函数时,若此时该对象指针指向基类对象,则系统就调用基类中的相应成员函数;若此时基类对象指针指向派生类对象,则系统就调用派生类中的相应成员函数。,【例5.2】 虚函数范例。 /- /c0502.cpp /- #include class A public:

13、 /构造函数 A(void),/定义虚函数 virtual void f1(void) const cout“基类的f1( )函数“endl; /定义一般成员函数 void f2(void) const cout“基类的f2( )函数“endl; ;,/使用虚函数时类层次间是公有继承 class AA: public A public: AA(void) / f1为虚函数 void f1(void)const cout“派生类的f1( )函数“endl; ,/f2为成员函数覆盖继承 void f2(void) const cout“派生类的f2( )函数“endl; ; void main(v

14、oid) ,A *pa, myA; /定义基类指针pa和基类对象myA AA myAA; /定义派生类对象myAA pa= /调用基类的f2()函数,myA.f2(); /调用基类的f2()函数 myAA.f2(); /调用派生类的f2()函数 程序运行后的结果是: 基类的f1()函数 基类的f2()函数 派生类的f1()函数 基类的f2()函数 基类的f2()函数 派生类的f2()函数,说明: (1) 上面例程中,派生类中f1()函数前未有关键字virtual但仍是虚函数。因为在派生类中声明的某个成员函数在其函数名、参数个数、参数类以及返回类型和基类中声明的虚函数完全一样,那么系统自动视作虚

15、函数。 (2) 从上面的例程可以看出,虚函数标示的滞后联编在运行时依据对象指针当前所指的对象是基类或派生类的,则调用对应的基类或派生类的成员函数。而一般的成员函数的覆盖继承则在编译时就依据对象指针的类型决定了始终调用某一类对象的成员函数。,如例中f2()函数是一般覆盖继承的函数,那么不管基类指针pa当前指向派生类或是基类对象,利用pa调用f2()函数时始终执行的是基类的f2()函数。,5.2.3 虚函数和覆盖继承的区别 虚函数也像派生类对基类成员函数的覆盖那样,在派生类中覆盖了基类的虚函数。但由于系统对虚函数采用滞后联编的编译方式,因此二者是有很大差别的。 对于派生类对基类成员函数的覆盖,调用成员函数的对象或对象指针在程序编译时就已确定,它对函数的选择是基于指向对象的指针(或引用)的类型,即要么是基类对象,要么是派生类对象,只能属于固定的一种,不能在程序运行时改变。,如果某对象或对象指针调用了一个覆盖的成员函数,则编译时系统的匹配方法是自该对象或对象指针定

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 高等教育 > 大学课件

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号