学员作品 学员心得技术园地 潮人地 Android(安卓)培训 杭州电脑(计算机)培训 杭州网络营销培训 杭州JAVA培训
返回首页
当前位置: 主页 > 学员天地 > 技术园地 >

java c++虚函数表剖析

时间:2017-11-24 17:47来源:未知 作者:杭州华育 点击:
一。概述 为了实现c++的动态,c++使用了一种动态绑定技术。这个技术的核心是虚函数表,(下文简称虚表)。本文介绍虚函数表是如何实现动态绑定的。 二,虚表的类型 虚表有哪些类型,

一。概述

为了实现c++的动态,c++使用了一种动态绑定技术。这个技术的核心是虚函数表,(下文简称虚表)。本文介绍虚函数表是如何实现动态绑定的。

二,虚表的类型

虚表有哪些类型,每个包含了虚函数的类都包含一个虚表。

我们知道,当一个类(A)继承另一个类(B)时,类A会继承类B的函数的调用权。所以如果一个基类包含了虚函数,那么其继承类也可调用这些虚函数,换句话说,一个类继承了包含虚函数的基类,那么这个类也拥有自己的虚表。

我们来看以下的代码。类A包含虚函数vfunc1,vfunc2,由于类A包含虚函数,故类A拥有一个虚表。

java学习路线图
类a的虚表如下图;
 
JAVA语言代码

虚表是一个指针数组,其元素是函数的指针,每个元素对应一个虚函数的函数指针。需要指出的是,普通的函数即非虚函数,其调用并不需要经过虚表,所以虚表的元素并不包括普通函数的函数指针。

虚表内的条目,即虚函数指针的的赋值发生在编译阶段,也就是说在代码的编译阶段,虚表就可以构造出来。

三  虚表指针

虚表是属于类的,而不是属于某个具体的对象,一个类只需要一个虚表即可。同一个类的所有对象都使用同一个虚表。

为了指定对象的虚表,对象内部包含一个虚表的指针,来指向自己所使用的虚表。为了让每个包含虚表的类的对象都拥有一个续表指针,编译器在类中添加了一个指针,*-vprt,用来指向虚表。这样,当类的对象在创建时便拥有了这个指针,且这个指针的值会自动被设置为指向类的虚表。

类a的一个对象

 

JAVA语言代码

上图:对象与它的虚表

上面指出,一个继承类的基数如果包含虚函数,那么这个继承类也有拥有自己的虚表,故这个继承类的对象也包含一个虚表指针,用来指向它的虚表。

四,动态绑定

说到这里,各位一定好奇c++是如何利用虚表和虚表指针来实现动态绑定的,大家看下面这行代码就明白了:

JAVA语言代码

类a是基类,类b继承类a,类c又继承类b。类a 类b 类c ,其对象模型如下图3所示。

 

JAVA语言代码

图3:类a 类b 类c的对象模型

由于这三类都有虚函数,故编译器为每个类都创建了一个虚表。即类a的虚表(A VTBL),类b的虚表(bvtbl),类c的虚表(c vtbl)。类a,类b,类c,的对象都拥有一个虚表指针,*-vptr,用来指向自己所属类的虚表。

类a包括两个虚函数,故A VTBL 包含两个指针,分别指向a:;vfuncl()和a::vfunc2().

类b继承于类a,故类b可以调用类a的函数,但由于类b重写了b::vfuncl()函数,故b vtbl 的两个指针分别指向b::vfuncl()和a::vfunc2()。

类C继承于类B,故类C可以调用类B的函数,但由于类C重写了C::vfunc2()函数,故C vtbl的两个指针分别指向B::vfunc1()(指向继承的最近的一个类的函数)和C::vfunc2()。 
虽然图3看起来有点复杂,但是只要抓住“对象的虚表指针用来指向自己所属类的虚表,虚表中的指针会指向其继承的最近的一个类的虚函数”这个特点,便可以快速将这几个类的对象模型在自己的脑海中描绘出来。

非虚函数的调用不用经过虚表,故不需要虚表中的指针指向这些函数。

假设我们定义一个雷b的对象。由于bobject 是类b的一个对象,故bogject包含一个虚表指针,指向类b的虚表。

 
JAVA语言代码
 

现在,我们声明一个类a的指针p来指向对象bobject。虽然p是基类的指针只能指向基类部分,但是虚表指针亦属于基类部分,所以p可以访问到对象bobject的虚表指针。bobject的虚表指针指向类b的虚表,所以p可以访问到b vtbl。如下图所示:

 

JAVA语言代码

当我们使用p来调用vfuncl()函数时,会发生什么现象?

 

JAVA语言代码

程序在执行p-》vfuncl()时,会发现p是个指针,且调用的函数是虚函数,接下来便会进行一下的步骤。

首先,根据虚表指针p-》-vprt 来访问对象bobject对应的虚表。虽然指针p是基类a*类型,但是*-vptr也是基类的一部分,所以可以通过p->_vptr可以访问到对象对应的虚表。

然后,在虚表中查找所调用的函数对应的条目。由于虚表在编译阶段就可以构造出来了,所以可以根据所调用的函数定位到虚表中的对应条目。对于p—>vfuncl()的调用,b vtbl的第一项即是vfuncl对应的条目。

最后,根据虚表中找到函数指针,调用函数。从 图3可以看出,b vtbl 的第一项即是vfuncl对应的条目。

最后,根据虚表中找到的函数指针,调用函数。从图3可以看到,b vtbl的第一项指向b::vfuncl(),所以 p->vfunc1()实质会调用B::vfunc1()函数。

如果p指向类a的对象,情况又是怎么样?

JAVA语言代码

当abject 在创建时,它的虚表指针__vptr已设置为指向a vtbl ,这样p->__vptr就指向A vtbl。vfunc1在A vtbl对应在条目指向了A::vfunc1()函数,所以 p->vfunc1()实质会调用A::vfunc1()函数。

可以把以上三个调用函数得步骤用以下表达式来表示;

 

JAVA语言代码

上面的图在显示:通过使用这些函数表,即使使用的是基类的指针来调用函数,也可以达到正确调用运行中实际对象的虚函数。

我们把经过虚表调用虚函数的过程称为动态绑定,其表现出来的现象称为运行时多太,动态绑定区别于传统的函数调用,传统的函数调用我们称之为静态绑定,即函数的调用在编译阶段就可以确定下来了。

那么,什么时候会执行函数的动态绑定?这需要符合以下三个条件。

通过指针来调用函数

指针upcast 向上转型(继承类向基类的转换称为upcast,关于什么是upcast,)可以查阅资料。

调用的是虚函数。

如果一个函数调用符合上面所述的三个条件,编译器就会把该函数调用编译成动态绑定,其函数的调用过程走的是上述通过虚表的机制。

在 封装,继承,多态是面向对象设计的三个特征,而多态可以说是面向对象设计的关键。c++通过虚函数表示,实现了虚函数与对象的动态绑定,从而构建了c++面向对象程序设计的基石。

 

(责任编辑:杭州华育 ;杭州java培训,网络营销培训,杭州计算机培训,it培训,详询客服报名咨询
分享到:
顶一下
(0)
0%
踩一下
(0)
0%
------分隔线----------------------------
提交报名信息

2017年杭州将加大力度经济转型,吸纳更多的技术人才落户杭州,软件人才需求量更是大幅提升,针对目前杭州IT行业人才需求“井喷”现象,华育软件杭州实训基地与杭州155家IT名企合作,建立人才培养与输送关系,启动“Eduask国家高端IT紧缺人才培养工程”。7月针对杭州地区18-28岁大中专生待业青年发放68个技能实训名额(应届大中专毕业生优先),经短期专业岗前实训后推荐进入企业,起薪4500-8000元,五险一金。

姓名: * 性别:
学历: 电话: *
所在地: *
您目前状况:
留言:
*