解析动态联编(上篇) - 作者:tingya

2016-01-29 12:18 4 1 收藏

解析动态联编(上篇) - 作者:tingya,解析动态联编(上篇) - 作者:tingya

【 tulaoshi.com - C语言心得技巧 】


解析动态联编(上篇)
作者:南京邮政局计算机中心 tingya
文章摘要
多态性是C++最主要的特征,多态性的实现得益于C++中的动态联编技术。文章通过对动态联编的关键技术虚拟函数表进行深入的剖析,解析的动态联编的过程极其技术要领。

关键字
多态性 动态联编 VTABLE 虚函数

文章正文

一 从多态性谈动态联编的必要性


在进入主题之前先介绍一下联编的概念。联编就是将模块或者函数合并在一起生成可 执行代码的处理过程,同时对每个模块或者函数调用分配内存地址,并且对外部访问也分配正确的内存地址。按照联编所进行的阶段不同,可分为两种不同的联编方法:静态联编和动态联编。在编译阶段就将函数实现和函数调用关联起来称之为静态联编,静态联编在编译阶段就必须了解所有的函数或模块执行所需要检测的信息,它对函数的选择是基于指向对象的指针(或者引用)的类型。反之在程序执行的时候才进行这种关联称之为动态联编,动态联编对成员函数的选择不是基于指针或者引用,而是基于对象类型,不同的对象类型将做出不同的编译结果。C语言中,所有的联编都是静态联编。C++中一般情况下联编也是静态联编,但是一旦涉及到多态性和虚函数就必须使用动态联编。
多态性是面向对象的核心,它的最主要的思想就是可以采用多种形式的能力,通过一个用户名字或者用户接口完成不同的实现。通常多态性被简单的描述为"一个接口,多个实现。在C++里面具体的表现为通过基类指针访问派生类的函数和方法。
下面我们看一个静态联编的例子,这种静态联编导致了我们不希望的结果。
//1.cpp1. #include <iostream.h2. 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.cpp1. #include <iostream.h2. 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.cpp1. #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_vi
                        

来源:http://www.tulaoshi.com/n/20160129/1485522.html

延伸阅读
标签: 月子
大姨妈or排卵期 谁决定谁?     排卵是备孕女性必须具备的条件之一,但是月经,却不是受孕最为充分的条件。意思就是说,若是女性没有出现排卵的现象,就算每个的月经按时的来临也不能受孕。那大姨妈or排卵期之间到时寻在这什么关系呢?下面一起去了解下!     一、有月经不一定有...
标签: 插画 颜色
首先我们看下完成图: 1.开始构图。打开Painter,新建一张A4大小300分辨率的画布,选择【铅笔】工具。先在脑子里想好大致的构图,然后画出大概的轮廓。 图1 2.将画笔的大小调整到2.0-3.0后,结合刚才的轮廓来画细节。 图2 3.一边画一边擦掉多余的线条。这种风格的图最后的线稿是不需要的,不过准确的线稿会使上色更方便。如果线...
奶妈酱逆袭《英雄联萌》新阵型培养攻略 哈哈哈,《英雄联萌》最新更新阵型系统三阵型齐发,当年那些个一心只培养输出英雄的大神们如今都傻眼了吧!当年秒我一时爽,如今看你怎么狂!小编我悉心调教多年的星妈酱终于要大放异彩了啊有木有!生命阵型升级血量噌噌往上涨啊有木有!秒不掉我就大奶跟你...
标签: 生活常识
双联开关的工作原理-双联开关的简介 双联开关的简介 双联开关有分为双联单控开关和双联双控开关。单联指一个按钮,单控:就是说它只有一个触点(常开触点或常闭触点)。双控:就是说它有两个触点(常开触点和常闭触点)。这些开关都是指按钮,带弹簧的那种。单联单控开关一般用于点动控制,双联单控开关一般用于长动控制。 ...
(上篇) 有很多朋友虽然安装好了mysql但却不知如何使用它。在这篇文章中我们就从连接MYSQL、修改密码、增加用户等方面来学习一些MYSQL的常用命令。 一、连接MYSQL。  格式: mysql -h主机地址 -u用户名 -p用户密码 1、例1:连接到本机上的MYSQL。 首先在打开DOS窗口,然后进入目录 mysqlbin,再键入命令mys...

经验教程

979

收藏

62
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部