OOP C++中的动态多态性及其底层实现机制研究

2014-10-13 08:26马瑞敏
长治学院学报 2014年2期
关键词:多态指针调用

马瑞敏

(长治学院 计算机系,山西 长治 046011)

OOP(Object Oriented Programming)的出现改变了编程者的思维方式,使设计程序的出发点由问题域中的过程转向问题域中的对象及其相互关系,更加符合人们对客观事物的认识,是目前主流的软件开发方法。多态性是OOP的三大重要概念(封装性、继承性、多态性)之一,理解并掌握好多态性的实现和应用方法在OOP过程中占有重要的地位。多态是指类族中具有相似功能的不同函数使用同一名称来实现,从而可以使用相同的调用方式来调用这些具有不同功能的同名函数,又称为“同一接口,多种方法”。

1 多态的类型

多态从实现的角度来讲可以划分为两类:编译时多态和运行时多态。前者是在编译时确定了同名操作的具体操作对象,又称静态多态,函数及运算符的重载、数据类型强制转换和模板都属于静态多态;后者是在程序运行过程中才能动态地确定操作所针对的具体对象,又称动态多态。总体上说,动态多态才是真正的多态性。

2 动态多态性的实现

下面根据程序Pro1.cpp来分析动态多态性的实现。

图1 未实现动态多态性的运行结果

图2 实现动态多态性的运行结果

在程序Pro1.cpp中派生类Derived1和Derived2虽然重写(重写是指函数名、参数及返回类型都与基类某函数相同但功能不同的函数)基类Base的成员函数,但根据赋值兼容与函数隐藏的规则,语句“pb->f();”只能访问到基类Base中的成员函数f(),不能访问到派生类中的同名成员函数,运行结果如图1所示,没有实现动态多态性。

要实现动态多态性,需要将基类中的同名函数定义为虚函数,即在基类的成员函数声明前加关键字virtual。同时还需三个先行条件:①派生类公有继承了基类;②在派生类中重写了同名虚函数;③用派生类对象地址为基类指针赋值,或用派生类对象初始化基类对象的引用。修改Pro1.cpp,在基类Base的成员函数f前加上virtual关键字,通过基类指针pb指向不同的派生类对象,可以访问到派生类中的虚函数,从而实现动态多态性。运行结果如图2所示。

C++规定:若在派生类中重写了基类的虚函数,即使不添加virtual关键字,它们也自动成为了虚函数。关键字virtual指示C++编译器对调用虚函数进行动态联编。上述条件③中对虚函数的调用也有约定:只能使用指针或引用调用虚函数,不能使用对象名调用。因为使用对象名调用函数时,函数调用与函数的实现在编译阶段已经确定,无法实现动态绑定,而指针或引用在程序运行过程中可能会指向同一基类的不同派生类对象,因此使得通过指针或引用调用虚函数时,对应的函数实现代码需要在程序运行过程中才能够进行绑定,从而实现动态多态性。一个声明或继承了虚函数的类称为多态类。

3 动态多态性底层实现机制研究

动态多态主要通过类的继承关系和虚函数来实现。虚函数的底层实现机制并未标准化。当前主流的C++编译器如Visual C++、Borland C++等实现虚函数的技术不尽相同,但基本都包含如下思想:程序运行时,编译器为每个多态类创建一个虚函数表vtable,表中按虚函数的声明顺序保存每个虚函数运行时在内存中的地址;同时还自动为该类增加一个指针类型的数据成员vptr,并让vptr指向虚函数表首地址。当用多态类定义的对象时,对象将自动有了一个指向虚函数表的指针成员vptr。如图3所示。

图3 虚函数实现机制

程序运行时,如果基类指针指向了这样的派生类对象并且要调用某个虚函数,就会通过派生类对象的vptr获得派生类中同名的虚函数地址,从而执行它,而不再理会基类中的该函数。仅当派生类中没有要调用的虚函数,程序才转去调用基类中的同名虚函数,也就是说派生类中虚函数表和指针成员vptr是实现运行时的多态性关键所在。下面分两种情况详细讨论Visual C++环境下虚函数的底层实现机制。

3.1 单继承下的动态多态性实现机制

假设存在如图4所示继承关系的两个类,派生类没有重写基类的任何虚函数,虚函数表中将按虚函数声明顺序存放其地址,派生类的虚函数表中父类的虚函数在排在前面,如图5所示。

图4 单继承关系下包含虚函数UML图

图5 单继承下虚函数实现机制

修改Derived类的成员函数名f1为f,表示Derived类重写基类的虚函数f(),在其虚函数表中重写的f()函数的地址将覆盖从基类继承来的虚函数的地址,没有被重写的函数依旧,如图6所示。当用基类指针pb指向Derived类的对象,调用虚函数即执行语句“pb→f();”时,vptr会在派生类的虚函数表中查找对应的函数的入口地址,调用对应派生类对象的成员函数f(),实现动态多态性。

图6 单继承下的动态多态性实现机制

3.2 多继承下的动态多态性实现机制

假设存在如图7所示多继承关系的三个类。Derived类重写了基类的虚函数f()。如果派生类的多个基类含有虚函数,编译系统会为它创建多个虚函数表。虚函数表中各虚函数的顺序与其基类中的一样,派生类中新增的虚函数将按声明顺序被追加在第一个虚函数表中。如果多继承关系中派生类重写了基类的某个虚函数,依然遵循覆盖原则,即在其每个虚函数表中用重写的虚函数的地址覆盖从基类继承来的同名虚函数的入口地址,如图8所示。

图7 多继承关系下包含虚函数UML图

图8 多继承下的动态多态性实现机制

4 结语

多态性是OOP的有力工具。适当利用多态性,可增加软件系统的灵活性,减少冗余信息,提高软件的可重用性和可扩充性。本文对C++语言中动态多态性的实现方法进行了分析,并通过相关实例和内存布局图对其在单继承和多继承关系下的底层实现机制进行了研究,给出了动态多态性设计和实现的有效方法。

[1]郑莉,董渊,何江舟.C++语言程序设计[M].北京:清华大学出版社,2010,(7):306-340.

[2]刘天印,李福亮.C++程序设计[M].北京:北京大学出版社,2006,(1):294-309.

[3]董泉伶.关于C++动态多态性实现机制的探究[J].现代计算机,2009,(11):108-110.

[4]赵红超,方金云,唐志敏.C++的动态多态和静态多态[J].计算机工程,2005,(10):72-74.

[5]宋新爱.基于容器的面向对象技术的多态性实现及应用[J].西安石油大学学报(自然科学版),2009,(9):86-88.

猜你喜欢
多态指针调用
参差多态而功不唐捐
核电项目物项调用管理的应用研究
LabWindows/CVI下基于ActiveX技术的Excel调用
为什么表的指针都按照顺时针方向转动
《C++面向对象程序设计》中引用类型的教学实践
基于系统调用的恶意软件检测技术研究
基于改进Hough变换和BP网络的指针仪表识别
人多巴胺D2基因启动子区—350A/G多态位点荧光素酶表达载体的构建与鉴定及活性检测
ARM Cortex—MO/MO+单片机的指针变量替换方法
利用RFC技术实现SAP系统接口通信