文章摘要
多态性是C++最主要的特征,多态性的实现得益于C++中的动态联编技术。文章通过对动态联编的关键技术虚拟函数表进行深入的剖析,解析的动态联编的过程极其技术要领。
关键字 多态性 动态联编 VTABLE 虚函数
文章正文
一 从多态性谈动态联编的必要性在进入主题之前先介绍一下联编的概念。联编就是将模块或者函数合并在一起生成可 执行代码的处理过程,同时对每个模块或者函数调用分配内存地址,并且对外部访问也分配正确的内存地址。按照联编所进行的阶段不同,可分为两种不同的联编方法:静态联编和动态联编。在编译阶段就将函数实现和函数调用关联起来称之为静态联编,静态联编在编译阶段就必须了解所有的函数或模块执行所需要检测的信息,它对函数的选择是基于指向对象的指针(或者引用)的类型。反之在程序执行的时候才进行这种关联称之为动态联编,动态联编对成员函数的选择不是基于指针或者引用,而是基于对象类型,不同的对象类型将做出不同的编译结果。C语言中,所有的联编都是静态联编。C++中一般情况下联编也是静态联编,但是一旦涉及到多态性和虚函数就必须使用动态联编。
多态性是面向对象的核心,它的最主要的思想就是可以采用多种形式的能力,通过一个用户名字或者用户接口完成不同的实现。通常多态性被简单的描述为"一个接口,多个实现。在C++里面具体的表现为通过基类指针访问派生类的函数和方法。
下面我们看一个静态联编的例子,这种静态联编导致了我们不希望的结果。
//1.cpp
1. #include <iostream.h>
2. class shape{
3. public:
4. void draw(){cout<<"I am shape"<<endl;}
5. void fun(){draw();}
6. };
7. class circle:public shape{
8. public:
9. void draw(){cout<<"I am circle"<<endl;}
10. };
11. main(){
12. class circle oneshape;
13. oneshape.fun();
14. }
程序的输出结果我们希望是"I am circle",但事实上却输出了"I am shape"的结果,造成这个结果的原因是静态联编。静态联编需要在编译时候就确定函数的实现,但事实上编译器在仅仅知道shape的地址时候无法获取正确的调用函数,它所知道的仅是shape::draw(),最终结果只能是draw操作束缚到shape类上。产生"I am shape"的结果就不足为奇了。
为了能够引起动态联编,我们只需要将需要动态联编的函数声明为虚函数即可。动态联编只对虚函数起作用。我们在通过基类而且只有通过基类访问派生类的时候,只要这个基类中直接的或者间接(从上上层继承)的包含虚函数,动态联编将自动唤醒。下面我们将上面的程序稍微改一下。
//2.cpp
1. #include <iostream.h>
2. class shape{
3. public:
4. virtual void draw(){cout<<"I am shape"<<endl;}
5. void fun(){draw();}
6. };
7. class circle:public shape{
8. public:
9. void draw(){cout<<"I am circle"<<endl;}
10. };
11. main(){
12. class circle oneshape;
13. fun(&oneshape);
14. }
程序执行得到了正确的结果"I am circle"。代码在VC6.0中执行。
到目前为止我们不清楚动态联编的执行机制,但我们可以做个猜测。正如上面所说,对于函数的实际的对象类型不同,联编结果也应该不同。在静态联编中,执行的困难在于无法通过基类知道需要联编的子对象的确切类型。在1.cpp中shape的派生类既可能是circle,也可能是其余的rectangle或者square等等,到底应该静态联编哪一个呢。迷惑正在于此。动态联编在编译的时候应该也是不知道联编的确切对象类型的,(如果知道的话就成了静态联编了),因此它只能通过一定的机制,使得在执行时候能够找到和调用正确的函数体。可以想象,为了达到这个目的,一些相关信息应该封装在对象自身中。这些信息有点象身份证明,标识自己,这样在动态联编的时候,编译器可以根据这些标记找到相应的函数体,"不要跑,就是你了"。
实际上的动态联编过程是什么样的呢。
二 对象类型信息
为了证明我们的猜想,我们用下面的一个程序进行测试,下面的程序将获取普通的类和包含虚函数的类的字节大小。程序代码如下。
//3.cpp
1. #include <iostream.h>
2. class shape_novirtual{
3. int a;
4. public:
5. void draw(){cout<<"shape_novirtual::draw()"<<endl;}
6. };
7. class shape_virtual1{
8. int a;
9. public:
10. virtual void draw(){cout<<"shape_virtual::draw()"<<endl;}
11. };
12. class shape_virtual2{
13. int a;
14. public:
15. virtual void draw(){cout<<"shape_virtual2::draw()"<<endl;}
16. virtual void draw1(){cout<<"shape_virtual2::draw1()"<<endl;}
17. };
18. main(){
19. cout<<"sizeof(int)"<<sizeof(int)<<endl;
20. cout<<"sizeof(class shape_novirtual):"<<sizeof(shape_novirtual)<<endl;
21. cout<<"sizeof(void*):"<<sizeof(void*)<<endl;
22. cout<<"sizeof(class shape_virtual):"<<sizeof(shape_virtual)<<endl;
23. cout<<"sizeof(class shape_virtual2):"<<sizeof(shape_virtual2)<<endl;
24. }
VC6.0中运行结果如下:
sizeof(int)4
sizeof(class shape_novirtual):4
sizeof(void*):4
sizeof(class shape_virtual1):8
sizeof(class shape_virtual2):8
Press any key to continue
从上面可以看出,没有虚函数的类shape_novirtual的大小为4,正好为int a的大小。而带有虚函数的类shape_virtual1和shape_virtual2的大小除了int a的大小还多出了4格个字节的大小,这个大小正好是void*指针的大小。到现在为止我们基本上可以说带有虚函数的对象自身确实插入了一些指针信息,而且这个指针信息并不随着虚函数的增加而增大。
如果我们将每个类的成员变量int a去掉,VC6.0运行结果就会变成下面的情况。
sizeof(int)4
sizeof(class shape_novirtual):1
sizeof(void*):4
sizeof(class shape_virtual1):4
sizeof(class shape_virtual2):4
Press any key to continue
上面的运行结果应该让人感到例外。既然size(int)为4,现在没有了这个成员变量,类shape_novirtual应该字节大小为0,但事实上C++编译器不允许对象为零长度。试想一个长度为0的对象在内存中怎么存放?怎么获取它的地址?为了避免这种情况,C++强制给这种类插入一个缺省成员,长度为1。如果有自定义的变量,变量将取代这个缺省成员。
作者信息:
电子邮件: flydish1234@sina.com.cn
分享到:
相关推荐
综上所述,这篇文章对软PLC中梯形图的动态解析方法进行了深入的研究,提出了一种基于逻辑二叉树的解析方法。该方法有效降低了传统方法对硬件资源的依赖,提高了梯形图解析的效率和准确性,对智能控制领域特别是暖通...
【曹县事业编招聘2018年考试真题及答案解析】 这是一份关于曹县2018年事业单位招聘考试的真题及答案解析文档。事业单位招聘考试通常涉及多个科目,包括行政职业能力测试、公共基础知识、专业知识等。这份文档可能是...
为了深入解析入库单的制作流程,本篇PPT教案将提供详尽的解说和操作指导。 首先,我们需认识到入库单在仓储管理中的核心作用。作为货物进入仓库并成为库存的法律依据,入库单上记录的信息将对后续的库存管理和物流...
【进出口货物报关单填制知识解析】 报关单在国际贸易中扮演着至关重要的角色,它是企业与海关间沟通的桥梁,确保货物按照适用的海关管理制度顺利通关。本篇主要阐述了进出口货物报关单的基本概念、内容用途、填制...
### 部编本人教版八年级上册语文《与朱元思书》知识点解析 #### 一、文体知识及背景介绍 - **文体特点**: - **骈文**:盛行于南北朝时期的特殊文体,以四字句、六字句相互交替,因此又称“四六文”。骈文的主要...
【标题与描述解析】 标题和描述中提到的是“河北省承德市联校 高二英语下学期期末考试试题(扫描版) 试题.doc”,这显然是一份针对中国高中二年级学生的英语期末考试试卷,由河北省承德市的联合学校编制。这份资料...
以下是对报告中提及的会计知识点的详细解析: 1. **基础会计工作**:会计人员需完成日常会计工作,如发票管理(抵扣联传递、登记)、凭证录入、汇兑损益结转、应交税费明细表的编制等,这些是会计基础操作,确保...
2. **运行时的多态性** 主要是通过**虚函数**(Virtual Function)实现,这是一种动态联编(Dynamic Binding)的技术。虚函数允许子类继承基类并覆盖基类的同名函数,当通过基类指针或引用调用这个函数时,实际执行...
### 会计述职报告关键知识点解析 #### 一、会计述职报告概述 会计述职报告是会计人员根据自身在一定时期内的工作情况进行总结并向上级汇报的一种书面文档。它不仅反映了会计人员的工作业绩,还体现了其对会计准则...
以下是对该文件部分内容的详细解析: 在20xx年的技术述职报告中,报告人重点阐述了在信息技术教育领域的三项主要成就: 1. **首次信息技术课堂技能大赛**:这是提升教师专业技能的重要活动,通过组织竞赛,不仅有...
这篇文档涉及的是高中数学的考试试题,主要涵盖了概率论、统计学和解析几何等知识点。以下是对这些内容的详细解析: 首先,试题中涉及到独立事件的概率计算。在问题(I)中,事件A、B、C分别代表甲、乙、丙三位面试...
通过RNA pull-down实验、蛋白质一维SDS-PAGE电泳结合液相色谱-质谱联用(1DE/LC/MS)以及二维电泳结合基质辅助激光解析电离飞行时间质谱(2DE/MALDI-TOF-MS)等技术,研究人员发现10个特异性结合蛋白,其中包括6个与...
- 第九题说明要实现动态联编,基类和派生类的函数需有相同的名称、返回类型以及参数,这是正确的。 - 第十四题提到对象数组可以赋初值但不能赋值,这个描述是错误的,对象数组可以进行赋值操作。 6. **构造函数与...
这篇文档是湖北省武汉市钢城第四中学2019-2020学年高二数学下学期5月的考试试题,包含单选题和填空题。以下是试题中涉及的知识点解析: 1. 组合计数:第一题是组合问题,计算三层书架上不同取书方法的总数,涉及到...
这篇文档是关于吉林省白城市2020-2021学年高二数学下学期期末考试的试题,主要涵盖单选题、填空题和解答题等多种题型,涉及的知识点广泛,包括概率论、逻辑推理、数列、复数、函数、平面几何、坐标系、线性代数等多...
这篇文档包含了华夏会计网会计证继续教育的考试题目,涵盖了会计领域的多个知识点,包括生产类型的区分、费用分类、成本计算方法、库存商品核算、发票管理、支票使用规范、记账凭证的编制以及成本会计的任务和成本...
### 110KV南湖变电站工程项目解析 #### 一、编制依据 在进行110KV南湖变电站的工程实施时,编制依据主要包括以下几个方面: 1. **湖北省电力公司城网改造招标工作组**发布的《110KV南湖变电站工程施工招标文件》...
本篇将详细解析以深圳大鹏海关为例的出口报关流程,包括一般贸易和转关报关流程,以及报关单的重要内容。 1. **一般贸易出口报关流程** - **查电脑舱单**:确保重柜已还场,并且有重柜纸,同时确认海关系统中有...
本篇文章将基于一份关于“SQL入门”的PDF资料进行深入解析,旨在为初学者提供一个全面了解如何使用SQL与DB2交互的基础。 #### 二、关键知识点详解 ##### 1. SQL基础概念 - **SQL**:一种标准化的数据访问和操纵...