多态性给我们带来了好处:多态使得我们可以通过基类的引用或指针来指明一个对象(包含其派生类的对象),当调用函数时可以自动判断调用的是哪个对象的函数。
一个函数说明为虚函数,表明在继承的类中重载这个函数时,当调用这个函数时应当查看以确定调用哪个对象的这个函数。
普通函数的处理:一个特定的函数都会映射到特定的代码,无论时编译阶段还是连接阶段,编译器都能计算出这个函数的地址,调用即可。
虚函数的处理:被调用的函数不仅依据调用的特定函数,还依据调用的对象的种类。通常是由虚函数表(vtable)来实现的。
虚函数表的结构:它是一个函数指针表,每一个表项都指向一个函数。任何一个包含至少一个虚函数的类都会有这样一张表。需要注意的是vtable只包含虚函数的指针,没有函数体。实现上是一个函数指针的数组。虚函数表既有继承性又有多态性。每个派生类的vtable继承了它各个基类的vtable,如果基类vtable中包含某一项,则其派生类的vtable中也将包含同样的一项,但是两项的值可能不同。如果派生类重载(override)了该项对应的虚函数,则派生类vtable的该项指向重载后的虚函数,没有重载的话,则沿用基类的值。
每一个类只有唯一的一个vtable,不是每个对象都有一个vtable,恰恰是每个同一个类的对象都有一个指针,这个指针指向该类的vtable(当然,前提是这个类包含虚函数)。那么,每个对象只额外增加了一个指针的大小,一般说来是4字节。

在类对象的内存布局中,首先是该类的vtable指针,然后才是对象数据。

在通过对象指针调用一个虚函数时,编译器生成的代码将先获取对象类的vtable指针,然后调用vtable中对应的项。对于通过对象指针调用的情况,在编译期间无法确定指针指向的是基类对象还是派生类对象,或者是哪个派生类的对象(见代码中的函数f在编译期间是无法判断的)。但是在运行期间执行到调用语句时,这一点已经确定,编译后的调用代码能够根据具体对象获取正确的vtable,调用正确的虚函数,从而实现多态性。

给出实例代码:
class A {
public :
virtual void run (){......}
}
class B :public A{
public:
void run(){......}
}
int f (A *pA){
pA->run();
}
分析一下这里的思想所在,问题的实质是这样,对于发出虚函数调用的这个对象指针,在编译期间缺乏更多的信息,而在运行期间具备足够的信息,但那时已不再进行绑定了而是直接执行好了,怎么在二者之间作一个过渡呢?把绑定所需的信息用一种通用的数据结构记录下来,该数据结构可以同对象指针相联系,在编译时只需要使用这个数据结构进行抽象的绑定,而在运行期间将会得到真正的绑定。这个数据结构就是vtable,也就是编译期间建立vtable表,执行期间查表执行。可以看到,实现用户所需的抽象和多态需要进行后绑定,而编译器又是通过抽象和多态而实现后绑定的。
下面是通过基类的指针来调用虚函数时,所发生的一切:
step 1:开始执行调用 pA->run();(这里能判断到底是哪个对象)
step 2:取得对象的vtable的指针
step 3:从vtable那里获得函数入口的偏移量,即得到要调用的函数的指针
step 4:根据vtable的地址找到函数,并调用函数。
step 1和step 4对于一般函数是一样的,虚函数只是多了step 2和step 3。
解惑:
1基类和派生类是共用一表,还是各有各的表(物理上)
答:基类和派生类是各有各的表,也就是说他们的物理地址是分开的,基类和派生类的虚表的唯一关联是:当派生类没有实现基类虚函数的重载时,派生类会直接把自己表的该函数地址值写为基类的该函数地址值.
 

C++多态性:虚函数的调用原理的更多相关文章

  1. C++中虚函数的作用和虚函数的工作原理

    1 C++中虚函数的作用和多态 虚函数: 实现类的多态性 关键字:虚函数:虚函数的作用:多态性:多态公有继承:动态联编 C++中的虚函数的作用主要是实现了多态的机制.基类定义虚函数,子类可以重写该函数 ...

  2. 漫谈 C++ 虚函数 的 实现原理

    文中讲述的原理是推理和探讨 , 和现实中的实现不一定完全相同 . C++ 的 虚函数 , 编译器 会 生成一个 虚函数表 . 虚函数表, 实际上是 编译器 在 内存 中 划定 的 一块 区域, 用于存 ...

  3. C++虚函数的工作原理

    静态绑定与动态绑定 讨论静态绑定与动态绑定,首先需要理解的是绑定,何为绑定?函数调用与函数本身的关联,以及成员访问与变量内存地址间的关系,称为绑定. 理解了绑定后再理解静态与动态. 静态绑定:指在程序 ...

  4. MFC浅析(7) CWnd类虚函数的调用时机、缺省实现

    CWnd类虚函数的调用时机.缺省实现 FMD(http://www.fmdstudio.net) 1. Create 2. PreCreateWindow 3. PreSubclassWindow 4 ...

  5. C++ 之虚函数的实现原理

    c++的多态使用虚函数实现,通过“晚绑定”,使程序在运行的时候,根据对象的类型去执行对应的虚函数. C++ 之虚函数的实现原理 带有虚函数的类,编译器会为其额外分配一个虚函数表,里面记录的使虚函数的地 ...

  6. 多态性&& 虚函数 && 抽象类

    http://www.cnblogs.com/CaiNiaoZJ/archive/2011/08/11/2134673.html 多态性 指相同对象收到不同消息或不同对象收到相同消息时产生不同的实现动 ...

  7. Direct3D Draw函数 异步调用原理解析

    概述 在D3D10中,一个基本的渲染流程可分为以下步骤: 清理帧缓存: 执行若干次的绘制: 通过Device API创建所需Buffer: 通过Map/Unmap填充数据到Buffer中: 将Buff ...

  8. C++虚函数实现多态原理(转载)

    一.前言 C++中的虚函数的作用主要是实现了多态的机制.关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数.这种技术可以让父类的指针有"多种形态 ...

  9. c++多态性---虚函数

    虚函数与纯虚函数的区别: 1.拥有虚函数的类可以声明对象,但拥有纯虚函数的类不可以声明对象(只能声明一个指针,并且不能给其分配内存),并且将这个类称为抽象类 特点: 1.虚函数是动态绑定的基础. 2. ...

随机推荐

  1. oracle sql修改序列为当前序列开始

    declare   v_num integer;  last_value integer;Begin  select SEQ_TBM_ID.NEXTVAL into last_value from d ...

  2. Android组件系列----ContentProvider内容提供者【4】

    (4)单元測试类: 这里须要涉及到另外一个知识:ContentResolver内容訪问者. 要想訪问ContentProvider.则必须使用ContentResolver. 能够通过ContentR ...

  3. 使用 sigaction 函数实现可靠信号

    前言 在前文中,讲述了一个可靠信号的示例.它分成几个步骤组成( 请参考前文 ).在 Linux 系统编程中,有个方法可以将这些步骤给集成起来,让我们使用起来更加的方便.那就是调用 sigaction ...

  4. 51NOD 1810 连续区间 分治 区间计数

    1810 连续区间 基准时间限制:1.5 秒 空间限制:131072 KB 分值: 80     区间内所有元素排序后,任意相邻两个元素值差为1的区间称为“连续区间” 如:3,1,2是连续区间,但3, ...

  5. 九度OJ 1133:学分绩点 (加权平均数)

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:1333 解决:702 题目描述: 北京大学对本科生的成绩施行平均学分绩点制(GPA).既将学生的实际考分根据不同的学科的不同学分按一定的公式 ...

  6. 操作系统 资源管理 zookeeper yarn 进程管理 分布式 yarn诞生背景

    zookeeper 信息保管员 YARN 简介 https://www.ibm.com/developerworks/cn/data/library/bd-yarn-intro/index.html

  7. Refusing to install package with name “XXXX”

    我的执行步骤 我创建了一个名叫express的文件夹,想在这个工程中学习express 进入该文件夹,执行npm init来初始化package.json文件,一直回车. 我们会发现当前文件夹多了一个 ...

  8. Eclipse内存错误java heap space

    Eclipse安装路径下的内存配置文件:eclipse.ini 文件末尾: -XX:MaxPermSize=256m-Xms40m-Xmx512m 其中 -Xmx512m表示最大内存,改为768或10 ...

  9. request,session,application三者关系<转>

    几乎所有的Web开发语言都支持Session功能,Servlet也不例外. Servlet/JSP中的Session功能是通过作用域(scope)这个概念来实现的. 对象作用域为:  page  在当 ...

  10. apache下实现301永久性重定向的方法

    因为博客是使用了www.php100.com作为博客域名,所以想实现php100.com全部重定向(跳转)到www.php100.com.同时按照google的建议,使用服务器端 301 重定向,为了 ...