




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、C+的多态性探讨朱 明(湖北大学 数学与计算机科学学院,武汉 430062)摘 要:C+是一种面向对象的程序设计语言,具有封装性,继承性,多态性三大基本特征,多态性是其最重要的特征之一,本文阐述了多态性与联编技术的关系,多态性的实现形式,着重讨论了动态多态性中虚函数的使用,以及虚指针的内部实现机制。关键词:多态性;联编;虚函数;虚指针1引 言在现实世界中,多态性是一种普遍存在的现象。比如,得知学期快要结束,即将放假的消息,学生想到的是要复习好各门功课,然后参加考试;老师想到的是要做好命题,监考,阅卷的工作;教务部门想到的是要及时,合理地制定出考试安排;学工,后勤部门想到的是要做好学生离校的有关
2、工作,等等;C+作为面向对象的程序设计语言,具有很强的描述现实世界的能力,多态性是其三大基本特征(封装、继承、多态性)之一对于多态性存在各种解释:如消息是指对类中成员函数的调用,而多态性指的是同一个消息被不同类型的对象接收时会导致不同的的行为实现;也有将多态性解释为多种形态的;或者,多态性是指一个标识符具有多种状态,而状态指的是该标识符被使用时采用的结合方式或产生的调用,等等。从程序设计最一般的角度考虑,其最基本的任务之一是要解决程序正文中的符号与机器内部存储之间的映射关系,即如何将程序空间的正文符号通过编译或解释映射成为代码空间的存储或操作。传统的面向过程的程序设计语言,如C语言,在处理程序
3、正文标识符与机器代码之间的映射时采用的是一对一的映射。如:int x=1;将程序正文中的标识符x与内存中的一个整型单元联系到一起,并且将初值1存放到该内存单元。在这里,x与其对应的整型单元之间的映射关系是一对一的映射关系。然而,在面向对象的程序设计语言中,如C+,程序正文标识符与机器代码之间一对一的映射关系被突破。一方面,多个程序正文标识符可以映射到同一内存单元或存储区域,形成程序空间到代码空间的多对一映射,引用机制的出现就是一个典型的,最具代表性的例证;另一方面,C+也允许程序空间到代码空间的一对多映射,这就是C+的多态性。为此,C+提供了支持(或实现)多态性的语法机制,它们是:静态联编和动
4、态联编。多态性的实现与联编技术有关。联编也称为绑定(binding),指的是如何将程序正文中一个具有多态性的标识符与代码空间中多个不同实现中的某一个联系起来的过程。根据联编所进行的阶段的不同,可分为静态联编和动态联编。静态联编是指在程序编译阶段进行的联编,这种联编过程是在程序运行之前完成的,又称为早期联编(early binding),其优点是效率高,但灵活性差;动态联编是指在程序运行阶段进行的联编,根据运行时的情况来决定究竟应该映射到哪一个具体实现,又称为滞后联编(late binding),其优点是灵活性强,但效率不高。根据所采用的联编技术的不同,可以将C+的多态性分为静态多态性和动态多态
5、性。2静态多态性静态多态性采用静态联编技术,是指在程序编译阶段就可确定的多态性,又称为编译时的多态性。它有两种实现形式:强制多态和重载多态。2.1强制多态将一种类型的值强制转换成为另一种类型的值称为类型强制。在基本数据类型之间可以通过显式的强制类型转换实现类型强制。如:对于int x;用x=(int)3.14;或x=int (3.14);或x=static_cast(3.14);都可以完成将浮点数3.14转换为整型数3。在使用类类型时也同样存在类似的类型强制,只不过这种类型强制既可以通过显式的方式来完成,也可以隐式地由构造函数来自动转换,甚至可以根据需要在类中定义类型强制转换成员函数来完成指定
6、的类型强制。参见下例:例1#include iostream.hclass A public:A(int x)a=x; couta constructor of class A is called!n; /构造函数用于类型强制operator int()couta operator int() is called!n; return a; /类型转换成员函数用于类型强制private:int a;void f(const A& x)coutfunction f is called!n;void main(void)A obj(1);f(2); /obj=a; /int m=obj; /运行结果
7、如下:1 constructor of class A is called!2 constructor of class A is called!function f is called!97 constructor of class A is called!97 operator int() is called!例1运行后共有5行输出,其中,代码A obj(1);被编译时,系统将自动调用构造函数创建A类对象obj,形成结果中第1行输出;处被编译时,由于在f函数中,形参是A类的引用,实参的类型与形参不一致,系统此时自动调用构造函数将常量2的类型int强制转换成为类类型A,具体过程是在main函
8、数工作区中创建一个A类的匿名对象,并以常量2为初值初始化该匿名对象,然后f函数的形参x引用该匿名对象完成虚实结合,执行f函数的函数体,最后返回mian函数,清除该匿名对象,形成结果中第2,3行输出;类似地,处被编译时,系统也会自动调用构造函数完成由char类型向A类型的类型强制,形成结果中第4行输出;处被编译时,在定义整型变量m并对其初始化时,初值是A类对象obj,因此系统将自动调用operator int()转换函数,将obj的类型由A强制转换成为int,然后再完成赋值操作,形成结果中第5行输出。2.2重载多态 重载实际上是给程序正文中相同作用域内的的同一个标识符赋予不同的操作语义。C+中重
9、载分为函数重载和运算符重载。从本质上讲,运算符重载也就是函数重载。用同一个标识符为多个函数命名称为函数重载,比如,有如下3个max函数:int max(int x,int y); /求2个整数的最大数int max(int x,int y,int z); /求3个整数的最大数int max(int n,int a); /求n个整数的最大数当通过相同的函数名调用被重载的多个函数时,编译器会以调用时的实参类型,个数和次序为依据进行重载解析,因此,不难想象,被重载的多个函数须在形参类型,个数和次序这三个方面不完全相同,否则将会出现二义性,即不能正确地重载解析。例2#include /using na
10、mespace std;class Circle /圆形类public: Circle(double a=0.0, double b=0.0, double r=0.0) x=a, y=b, radius=r; double area( ) return 3.14159*radius*radius; Circle & max(Circle &c) /函数重载,求2个Circle类对象的最大值 return area( )c.area( )? *this : c; friend ostream& operator (ostream&, Circle&); /运算符重载,将流插入运算符重载为该类的
11、友元函数private: double x,y;/圆心坐标 double radius;/半径;ostream& operator(ostream &output, const Circle &c)outputcenter:(c.x,c.y)area=c.area( )endl;return output;void main(void)Circle c1(1, 2, 3),c2(4, 5, 6),c3;c3=c1.max(c2); /coutc3; /运行结果为:center:(4,5) area=113.097,主函数中首先创建了3个Circle类对象c1,c2和c3;处被编译时,通过对象c
12、1调用重载的成员函数max,返回较大值对象c2,然后调用该类的缺省拷贝赋值操作给对象c3赋值;处被编译时,系统调用重载的流插入运算符函数完成对象c3的输出。3动态多态性动态多态性采用动态联编技术,是指在程序运行阶段才能确定的多态性,又称为运行时的多态性。它有两种实现形式:类型参数多态和包含多态。3.1类型参数多态C+是一种强类型语言,参与运算的所有数据的类型在编译时便已确定下来,只有类型匹配才能进行相应的运算,为此,编译器将进行严格的类型检查以确保程序语法的正确性,这样做的好处是显而易见的,保证了类型安全;但与此同时,也带来了一些副作用,增加了程序设计的工作量。例如,有两个求最大值函数,其语义
13、完全一样,只不过所带形参类型不同,也要写成两个函数,虽然可以重载,但毕竟还是两个函数,增加了程序的重复代码。为此,强类型语言通常采用类属机制来解决这个问题,在C+中,用类型参数多态即模板机制来实现类属,这种方法可以直接将数据类型作为类的参数或函数的参数使用,从而可把功能上一致,而只是在参数类型上有差异的类或函数声明为一个通用类或通用函数,即形成类模板或函数模板,它们只是一种抽象的描述,不能直接使用,必须先通过实例化创建某种实际类型的类或函数,方可使用。例3#include template /声明类模板class Comparepublic:Compare(T a, T b):x(a), y(
14、b) /声明函数模板T max( ) /声明函数模板return (xy)?x:y;private:T x, y;void main(void)Compare c1(3,7); /定义对象c1,用于两个整数的比较coutc1.max( ) endl;/Compare c2(45.78,93.6);/定义对象c2,用于两个浮点数的比较coutc2.max( ) endl; /Compare c3(a, A); /定义对象c3,用于两个字符的比较coutc3.max( ) endl; /运行结果为:793.6a处的语句有两个作用:一方面声明了一个虚拟类型参数T,另一方面也告诉C+编译器,接下来的声
15、明是一个通用类(即类模板)的声明;,两处声明的函数均用到虚拟类型T,也成为通用函数(即函数模板)的声明;主函数中分别用实际类型参数int,float,char替换虚拟类型参数T,实例化了类模板,同时也实例化了该类模板中的两个函数模板,并创建了3个对象c1,c2和c3;,处分别用对象c1,c2和c3调用模板函数max,得到上述输出结果。3.2包含多态要理解C+的包含多态,我们需要弄清楚函数覆盖的原则,先看一个例子: 例4#include class Animalpublic: void sleep()cout Animal sleependl; void breathe()cout Animal
16、 breatheendl;void jump()cout Animal jumpendl;class Cat:public animalpublic: void sleep()cout Cat sleependl;void breathe()cout Cat breathe sleep(); pa- breathe(); pa- jump();void main()Animal a; Cat c;f(&a); /f(&c); /运行结果为:Animal sleepAnimal breatheAnimal jumpAnimal sleepAnimal breatheAnimal jump程序中声
17、明了两个类:基类Animal和其公有派生类Cat;基类中声明了3个公有成员函数sleep( ),breathe( )和jump( ),派生类在继承的同时对前两个成员函数进行了重新定义。输出结果共有6行,前3行很好理解,主函数中首先创建了基类对象a,再以其地址作为实参调用函数f(即处),函数f的形参是指向基类对象的指针变量p,则在函数体中通过p访问到的理所当然地应该是基类中的3个成员函数,形成前3行输出结果;后3行输出是值得注意的地方,主函数中又创建了派生类对象c,其结构包括两部分:从基类继承而来的3个成员函数(即基类子对象部分)和重新定义的前两个成员函数(即新增成员部分),以sleep( )为
18、例,在派生类对象c中存在Animal:sleep( )和Cat:sleep( )两个sleep( )函数,值得注意的是它们不会形成重载函数,其原因是这两个函数的原型完全一致,为此,只能根据C+中同名函数覆盖的原则,当主函数中以c的地址作为实参调用函数f(即处)时,由于派生类与基类之间所具有的子类型关系,在虚实结合后,函数f的形参指针变量p就指向了实参对象c结构中的第一部分,即基类子对象部分,然后执行函数f的函数体,通过p访问到的也就还是基类中的sleep( )成员函数。函数breathe( )的情况与此类似,而函数jump( )在派生类中没有重新定义,不存在同名问题,对它的访问也就自然是访问到
19、基类中的jump( )成员函数了。由此,形成了上述的后3行输出结果。有时,我们希望通过基类指针指向类族中的不同类对象,然后以通过指针间接访问这种统一的方式来实现对类族中不同类的同名成员函数的调用,C+中用虚函数来解决这个问题。例如,通过关键字virtual将主函数中的三个成员函数声明为虚函数,修改如下:virtual void sleep()cout Animal sleependl;virtual void breathe()cout Animal breatheendl;virtual void jump()cout Animal jumpsleep()和p-breathe()时,根据虚表
20、中的函数地址找到的就是Cat类的sleep()和breathe()函数。实际上,可以通过观察多态类对象和普通类对象占据字节数(sizeof操作)的差异,来体会虚表指针(vptr)的存在。也可以通过在修改后的例4中增加以下代码,来使vptr在类外显形:void * get_p(void *p)return (void *) *(unsigned long *)p;typedef void (*FUNC_POINTER)();FUNC_POINTER get_funp(Animal *animal,unsigned long off)void * vptr=get_p(animal); char
21、*p=(char *)vptr; p+=sizeof(void *)*off; return (FUNC_POINTER)get_p(p);void display(Animal *animal) FUNC_POINTER p=get_funp(animal,0); p(); p=get_funp(animal,1); p(); p=get_funp(animal,2); p();void main(void) Animal *pa=new Animal; Animal *pc=new Cat; display(pa); display(pc); delete pa; delete pc; c
22、in.get();其运行结果和上述修改后的例4完全相同。随着问题抽象级别的提高,在类体系结构的顶层有时不能为虚函数给出一个有意义的实现。因此需要一种描述这种现象的特殊虚函数,即纯虚函数。纯虚函数仅仅指出了类体系结构中将存在的一种操作,至于该操作如何实现,以及该操作有多少种实现,这些则是派生类要解决的问题。包含纯虚函数的类称为抽象类,由于通常作为基类使用,又称为抽象基类。如在修改后的例4中,可增加抽象基类如下:class Creature /抽象基类public: virtual void sleep()=0; /纯虚函数 virtual void breathe()=0; /纯虚函数 virtual void jump()=0; /纯虚函数;然后修改Animal类的首行为class Animal:public Creature,此时,若在main()函数中增加语句Creature cre;则会出现语法错误,
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025版人力资源项目外包合同模板
- 人力资源服务合同正规格式指南2025
- 天然气购销标准合同
- 云南省昭通市昭阳区苏家院乡中学2024-2025学年初三年级下学期第二次月考试题含解析
- 铜仁学院《生物合成实验》2023-2024学年第二学期期末试卷
- 南阳工艺美术职业学院《急诊医学Ⅰ》2023-2024学年第二学期期末试卷
- 云南省临沧市达标名校2025届初三下学期期末学业质量监测生物试题理试题含解析
- 西安电子科技大学《行为医学》2023-2024学年第一学期期末试卷
- 内蒙古乌海市海南区2024-2025学年初三下学期第八次统练(一模)生物试题含解析
- 上海中医药大学《媒体展示策划》2023-2024学年第二学期期末试卷
- 新疆地区历年中考语文文言文阅读试题42篇(含答案与翻译)(截至2024年)
- 图解-“健康中国2030”规划纲要-医学课件
- 煤矿事故隐患排查治理制度培训课件
- 个人信用报告生成器:自动生成信用报告
- 基于ENSP的校园网设计与配置
- 《乳腺MRI诊断》课件
- 专题19:首字母填空 -2023年中考英语考试研究(原题版)(上海专用)
- 2024历年司考劳动法真题
- 卫星导航定位精度-洞察分析
- 四川政采评审专家入库考试基础题复习测试附答案
- 安装悬浮地板合同范例
评论
0/150
提交评论