多态性——vptr和vtable

转自:http://www.sf.org.cn/Article/base/200805/21024.html


多态性 (polymorphism) 是面向对象编程的基本特征之一。而在 C++ 中,多态性通过虚函数 (virtual function) 来实现。我们来看一段简单的代码:
  #include <iostream>
  using namespace std;
  class Base
  {
  int a;
  public:
  virtual void fun1() {cout<<"Base::fun1()"<<endl;}
  virtual void fun2() {cout<<"Base::fun2()"<<endl;}
  virtual void fun3() {cout<<"Base::fun3()"<<endl;}
  };
  class A:public Base
  {
  int a;
  public:
  void fun1() {cout<<"A::fun1()"<<endl;}
  void fun2() {cout<<"A::fun2()"<<endl;}
  };
  void foo (Base& obj)
  {
  obj.fun1();
  obj.fun2();
  obj.fun3();
  }
  int main()
  {
  Base b;
  A a;
  foo(b);
  foo(a);
  }
  运行结果为:
  Base::fun1()
  Base::fun2()
  Base::fun3()
  A::fun1()
  A::fun2()
  Base::fun3() 
  仅通过基类的接口,程序调用了正确的函数,它就好像知道我们输入的对象的类型一样!
  那么,编译器是如何知道正确代码的位置的呢?其实,编译器在编译时并不知道要调用的函数体的正确位置,但它插入了一段能找到正确的函数体的代码。这称之为 晚捆绑 (late binding) 或 运行时捆绑 (runtime binding) 技术。
  通过virtual 关键字创建虚函数能引发晚捆绑,编译器在幕后完成了实现晚捆绑的必要机制。它对每个包含虚函数的类创建一个表(称为VTABLE),用于放置虚函数的地址。在每个包含虚函数的类中,编译器秘密地放置了一个称之为vpointer(缩写为VPTR)的指针,指向这个对象的VTABLE。所以无论这个对象包含一个或是多少虚函数,编译器都只放置一个VPTR即可。VPTR由编译器在构造函数中秘密地插入的代码来完成初始化,指向相应的VTABLE,这样对象就“知道”自己是什么类型了。 VPTR都在对象的相同位置,常常是对象的开头。这样,编译器可以容易地找到对象的VTABLE并获取函数体的地址。
  如果我们用sizeof查看前面Base类的长度,我们就会发现,它的长度不仅仅是一个int的长度,而是增加了刚好是一个void指针的长度(在我的机器里面,一个int占4个字节,一个void指针占4个字节,这样正好类Base的长度为8个字节)。
  每当创建一个包含虚函数的类或从包含虚函数的类派生一个类时,编译器就为这个类创建一个唯一的VTABLE。在VTABLE中,放置了这个类中或是它的基类中所有虚函数的地址,这些虚函数的顺序都是一样的,所以通过偏移量可以容易地找到所需的函数体的地址。假如在派生类中没有对在基类中的某个虚函数进行重写(overriding),那末还使用基类的这个虚函数的地址(正如上面的程序结果所示)。
    
  至今为止,一切顺利。下面,我们的试验开始了。
  就目前得知的,我们可以试探着通过自己的代码来调用虚函数,也就是说我们要找寻一下编译器秘密地插入的那段能找到正确函数体的代码的足迹。
  如果我们有一个Base指针作为接口,它一定指向一个Base或由Base派生的对象,或者是A,或者是其它什么。这无关紧要,因为VPTR的位置都一样,一般都在对象的开头。如果是这样的话,那么包含有虚函数的对象的指针,例如Base指针,指向的位置恰恰是另一个指针——VPTR。VPTR指向的 VTABLE其实就是一个函数指针的数组,现在,VPTR正指向它的第一个元素,那是一个函数指针。如果VPTR向后偏移一个Void指针长度的话,那么它应该指向了VTABLE中的第二个函数指针了。
  这看来就像是一个指针连成的链,我们得从当前指针获取它指向的下一个指针,这样我们才能“顺藤摸瓜”。那么,我来介绍一个函数:
  void *getp (void* p)
  {
  return (void*)*(unsigned long*)p;
  }
  我们不考虑它漂亮与否,我们只是试验。getp() 可以从当前指针获取它指向的下一个指针。如果我们能找到函数体的地址,用什么来存储它呢?我想应该用一个函数指针:
  typedef void (*fun)();
  它与Base中的三个虚函数相似,为了简单我们不要任何输入和返回,我们只要知道它实际上被执行了即可。
  然后,我们负责“摸瓜”的函数登场了:
  fun getfun (Base* obj, unsigned long off)
  {
  void *vptr = getp(obj);
  unsigned char *p = (unsigned char *)vptr;
  p += sizeof(void*) * off;
  return (fun)getp(p);
  }
  第一个参数是Base指针,我们可以输入Base或是Base派生对象的指针。第二个参数是VTABLE偏移量,偏移量如果是0那么对应fun1(),如果是1对应fun2()。getfun() 返回的是fun类型函数指针,我们上面定义的那个。可以看到,函数首先就对Base指针调用了一次getp(),这样得到了vptr这个指针,然后用一个 unsigned char指针运算偏移量,得到的结果再次输入getp(),这次得到的就应该是正确的函数体的位置了。
  那么它到底能不能正确工作呢?我们修改main() 来测试一下:
  int main()
  {
  Base *p = new A;
  fun f = getfun(p, 0);
  (*f)();
  f = getfun(p, 1);
  (*f)();
  f = getfun(p, 2);
  (*f)();
  delete p;
  }
  激动人心的时刻到来了,让我们运行它!
  运行结果为:
  A::fun1()
  A::fun2()
  Base::fun3()
  至此,我们真的成功了。通过我们的方法,我们获取了对象的VPTR,在它的体外执行了它的虚函数。

转载于:https://www.cnblogs.com/wangjixianyun/archive/2012/12/21/2827346.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/409309.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

rfcv函数实现_JAVA_用_JCO连接_SAP,实现调用SAP_的_RFC_函数(整理)(附一篇看起来比较全面的说明)(JCO报错信息)...

// 获取RFC返回的字段值11 JCoParameterList exportParam function.getExportParameterList();12 String exParamA exportParam.getString("field_A");13 String exParamB exportParam.getString("field_B");14 // 遍历RFC返回的表对象15 JCoTable tb …

南京林业大学转计算机专业好转吗,南京林业大学如何转专业

第二部分 录取规则一、我校坚持以高考成绩为主&#xff0c;德、智、体全面衡量&#xff0c;按照公平、公正、公开、择优的录取原则。二、普通类考生&#xff0c;统考成绩达到同批录取控制分数线&#xff0c;学校按照不超过计划数的120%的比例调阅考生档案&#xff0c;根据投档成…

abap 转换成字符串_ABAP--关于字符串String到XString XString to String转换代码

转自http://guanhuaing.iteye.com/blog/1498891代码如下report zrich_0001.data: s type string,h(1) type x,c(1) type c,byte(2) type c,length type i,l_bindata type xstring,l_cntbin TYPE sdokcntbins.FIELD-SYMBOLS: .s This Is A String!.length strlen( s ).* Use W…

如何禁用计算机的服务,如何彻底禁用电脑中的迅雷服务XLservicePlatform

‍有用户发现电脑系统安装了迅雷极速版或迅雷7之后就会出现一项服务XLservicePlatform&#xff0c;不仅默认开机自动启动&#xff0c;而且还占用CPU资源。一般的方法无法将其彻底禁用。对此&#xff0c;我们可以参考接下来提供的方法来彻底禁用电脑中的迅雷服务XLservicePlatfo…

[转载]oracle的表导入导出,表空间,用户名

原文地址&#xff1a;oracle的表导入导出&#xff0c;表空间&#xff0c;用户名作者&#xff1a;不小馨1.查询所有的表空间 select * from dba_data_files; 2.创建表空间 create tablespace 名称 logging datafile D:ORACLEPRODUCT10.2.0ORADATAMYDBtest.dbf size 100M reu…

finereport 格式化金额函数_帆软报表(finereport)常用函数

帆软报表FineReport中数据连接之Websphere配置JNDI连接以oracle9i数据源制作的模板jndi.cpt为例来说明如何在FineReport中的Websphere配置JNDI连接.由于常用服务器的JNDI驱动过大,帆软报表FineReport中没有自带, ...帆软报表FineReport SQLServer数据库连接失败常见解决方案1. …

唐山师范学院计算机二级报名,2017年3月唐山师范学院计算机等级考试报名时间(河北)...

唐山师范学院2017年上半年全国计算机等级考试(以下简称NCRE)将于3月份举行。按照教育部考试中心有关要求&#xff0c;现就报名工作有关事项通知如下&#xff1a;一、考试时间2017年上半年NCRE时间为3月25日-28日。二、报名事项说明(一)报名时间2017年上半年NCRE报名工作于2017年…

android ListView实现下拉上拉刷新功能

android ListView实现下拉上拉刷新功能 主ListView类&#xff1a;package com.carcare.refresh;/*** file XListView.java* package me.maxwin.view* create Mar 18, 2012 6:28:41 PM* author Maxwin* description An ListView support (a) Pull down to refresh, (b) Pull up …

一文简述npm和cnpm和yarn的区别

我是歌谣 放弃很容易 但是坚持一定很酷 前言 在前端的vue的项目中 不免会安装较多的文件依赖 对于常规安装依赖 我知道的方式有两种npm和yarn 1npm 1.1定义 npm: Nodejs下的包管理器。 1.2 安装 安装node环境 直接node官网安装一下 1.3 下载地址 node地址 下载安装好之后…

统计通话次数和时间的软件_通话时间统计app下载|通话时间统计安卓版下载 v1.0.3 - 跑跑车安卓网...

这是一款通话记录APP&#xff0c;用户可通过该软件来查看电话未接来电、通话时长等信息&#xff0c;十分便捷好用&#xff0c;有需要的快来下载使用吧&#xff01;软件介绍本程序用于通话记录、时长显示、分类统计、时段统计等参考。想知道最近你跟某人通了多长时间电话吗&…

idea html ajax,在 IntelliJ IDEA 8.1中编写一个ajax jquery实例,取不到页面上的值

在 IntelliJ IDEA 8.1中编写一个ajax jquery实例&#xff0c;取不到页面上的值0brnm12942014.08.17浏览115次分享举报public class AjaxServlet extends HttpServlet{protected void doGet(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)th…

Vue+Element!一千字带你编写合理的编辑,查看,新建!

我是歌谣 放弃很容易 但是坚持一定很酷 1前言 在我们的日常的开发过程中 不免会遇到需要操作同一页面得情况 2需求 比如一个页面 我们会有一个对应的一个表单 A:姓名:XXXX B&#xff1a;年龄:XXXX C:账号:XXXX D&#xff1a;密码:XXXX E:邮箱:XXXX XXXXXel-formel-input name…

script标签属性用type还是language?

一个网站的建设&#xff0c;经常会用到JavaScript,其中必须用到script标签来外调js文件&#xff0c;但是script标签属性用type还是language&#xff1f; type 和 language 属性都可用来指定 <script> 标签中的脚本的类型。所以可以使用下面两种属性&#xff1a; language…

惊呼!JavaScript基本数据类型和引用数据类型详解

前言 我是歌谣 知其然知其所以然 人人都有一个大厂梦 希望通过自己的一个总结分享可以给予大家带来帮助和提升。 本期知识点 JavaScript中基本数据类型和引用数据类型 目标 理解基本数据类型和引用数据类型的定义理解堆和栈数据类型分为哪些 定义 ECMAScript包括两个不同类型…

计算机的特点 分类及其应用,2016计算机知识:计算机的特点、分类及其应用(1)...

【导读】为了帮助广大考生更好的备考&#xff0c;中公事业单位考试网提供2016年计算机知识《计算机的特点、分类及其应用(1)》&#xff0c;为考生定制计算机基础知识复习计划&#xff0c;相信广大考生可以顺利计算机知识考试。一、第1代&#xff1a;电子管数字计算机(1946-1958…

面向船舶结构健康监测的数据采集与处理系统(一)系统架构

世界贸易快速发展起始于航海时代&#xff0c;而船舶作为重要的水上交通工具&#xff0c;有 其装载量大&#xff0c;运费低廉等优势。但船舶在运营过程中出现的某些结构处应力值 过大问题往往会给运营部门造成重大的损失&#xff0c;甚至造成大量的人员伤亡和严重 的环境污染…

Gunicorn 0.17 发布,Python HTTP 服务器

HTTP 服务器 Gunicorn 0.17 发布了&#xff0c;该版本改进内容包括&#xff1a; 支持绑定多个网卡地址支持 SSL支持 syslog增加 nworkers_changed 钩子为 post_request 钩子增加 response 参数使用 argparse 解析命令行参数&#xff08;废弃 optparse&#xff09;fix PWD detec…