带虚函数的类的对象布局(1)

  如果类中存在虚函数时,情况会怎样呢?我们知道当一个类中有虚函数时,编译器会为该类产生一个虚函数表,并在它的每一个对象中插入一个指向该虚函数表的指针,通常这个指针是插在对象的起始位置。所谓的虚函数表实际就是一个指针数组,其中的指针指向真正的函数起始地址。我们来验证一下,定义一个无成员变量的类C040,内含一个虚函数。
struct C040
{
    virtual void foo() {}
};
  运行如下代码打印它的大小及对象中的内容。
PRINT_SIZE_DETAIL(C040)
  结果为:
The size of C040 is 4
The detail of C040 is 40 b4 45 00
  果然它的大小为4字节,即含有一个指针,指针指向的地址为0x0045b440。

  同样再定义一个空类C050,派生自类C040。
struct C050 : C040
{
};
  由于虚函数会被继承,且维持为虚函数。那么类C050的对象中同样应该含有一个指向C050的虚函数表的指针。
  运行如下代码打印它的大小及对象中的内容。
PRINT_SIZE_DETAIL(C050)
  结果为:
The size of C050 is 4
The detail of C050 is 44 b4 45 00
  果然它的大小也为4字节,即含有一个指向虚函数表(后称虚表)的指针(后称虚表指针)。

  虚表是类级别的,类的所有对象共享同一个虚表。我们可以生成类C040的两个对象,然后通过观察对象的地址、虚表指针地址、虚表地址、及虚表中的条目的值(即所指向的函数地址)来进行验证。
  运行如下代码:
C040 obj1, obj2;
PRINT_VTABLE_ITEM(obj1, 0, 0)
PRINT_VTABLE_ITEM(obj2, 0, 0)
  结果如下:
obj1  : objadr:0012FDC4 vpadr:0012FDC4 vtadr:0045B440 vtival(0):0041D834
obj2  : objadr:0012FDB8 vpadr:0012FDB8 vtadr:0045B440 vtival(0):0041D834
  (注:第一列为对象名,第二列(objadr)为对象的内存地址,第三列(vpadr)为虚表指针地址,第四列(vtadr)为虚表的地址,第五列(vtival(n))为虚表中的条目的值,n为条目的索引,从0开始。后同)
  果然对象地址不同,虚表指针(vpadr)位于对象的起始位置,所以它的地址和对象相同。两个对象的虚表指针指向的是同一个虚表,因此(vtadr)的值相同,虚表中的第一条目(vtival(0))的值当然也一样。
  接下来,我们再观察类C040和从它派生的类C050的对象,这两个类各有自己的虚表,但由于C050没有重写继承自C040的虚函数,所以它们的虚表中的条目的值,即指向的虚函数的地址应该是一样的。
  运行如下代码:
C040 c040;
C050 c050;
PRINT_VTABLE_ITEM(c040, 0, 0)
PRINT_VTABLE_ITEM(c050, 0, 0)
  结果为:
c040   : objadr:0012FD4C vpadr:0012FD4C vtadr:0045B448 vtival(0):0041D834
c050   : objadr:0012FD40 vpadr:0012FD40 vtadr:0045B44C vtival(0):0041D834
  果然这次我们可以看到虽然前几列皆不相同,但最后一列的值相同。即它们共享同一个虚函数。

  定义一个C043类,包含两个虚函数。再定义一个C071类,从C043派生,并重写继承的第一个虚函数。
struct C043
{
    virtual void foo1() {}
    virtual void foo2() {}
};
struct C071 : C043
{
    virtual void foo1() {}
};
  我们可以预料到,C043和C071各有一个包含两个条目的虚表,由于C071派生自C043,并且重写了第一个虚函数。那么这两个类的虚表的第一个条目值是不同的,而第二项应该是相同的。运行如下代码。
C043 c043;
C071 c071;
PRINT_SIZE_DETAIL(C071)
PRINT_VTABLE_ITEM(c043, 0, 0)
PRINT_VTABLE_ITEM(c071, 0, 0)
PRINT_VTABLE_ITEM(c043, 0, 1)
PRINT_VTABLE_ITEM(c071, 0, 1)
  结果为:
The size of C071 is 4
The detail of C071 is 5c b4 45 00
c043   : objadr:0012FCD4 vpadr:0012FCD4 vtadr:0045B450 vtival(0):0041D4F1
c071   : objadr:0012FCC8 vpadr:0012FCC8 vtadr:0045B45C vtival(0):0041D811
c043   : objadr:0012FCD4 vpadr:0012FCD4 vtadr:0045B450 vtival(1):0041DFE1
c071   : objadr:0012FCC8 vpadr:0012FCC8 vtadr:0045B45C vtival(1):0041DFE1
  观察第1、2行的最后一列,即两个类的虚表的第一个条目,由于C071重写了foo1函数,所以这个值不一样。而第3、4行的最后一列为两个类的虚表的第二个条目,由于C071并没有重写它,所以这两个值是相同的。和我们之间的猜测是一致的。

  (未完待续)

潘凯:C++对象布局及多态实现的探索(二)相关推荐

  1. 潘凯:C++对象布局及多态实现的探索(十)

    菱形结构的虚继承(2) 我们再看一个例子,这个例子的继承结构和上一篇中是一样的,也是菱形结构.不同的是,每一个类都重写了顶层类声明的虚函数.代码如下: struct C041 {     C041() ...

  2. 潘凯:C++对象布局及多态实现的探索(九)

    菱形结构的虚继承 这次我们看看菱形结构的虚继承.虚继承的引入本就是为了解决复杂结构的继承体系问题.上一篇我们在讨论虚继承时用的是一个简单的继承结构,只是为了打个铺垫. 我们先看看这几个类,这是一个典型 ...

  3. 潘凯:C++对象布局及多态实现的探索(六)

    虚函数调用 我们再看看虚成员函数的调用.类C041中含有虚成员函数,它的定义如下: struct C041 {     C041() : c_(0x01) {}     virtual void fo ...

  4. 潘凯:C++对象布局及多态实现的探索(一)

    前言 本文通过观察对象的内存布局,跟踪函数调用的汇编代码.分析了C++对象内存的布局情况,虚函数的执行方式,以及虚继承,等等. 写这篇文章源于我在论坛上看到的一个贴子.有人问VC使用了哪种方式来实现虚 ...

  5. 潘凯:C++对象布局及多态实现的探索(三)

    带虚函数的类的对象布局(2) 接下来我们看看多重继承.定义两个类,各含一个虚函数,及一个数据成员.再从这两个类派生一个空子类. struct C041 {     C041() : c_(0x01) ...

  6. 潘凯:C++对象布局及多态实现的探索(十一)

    菱形结构的虚继承(3) 最后我们看看,如果在上篇例子的基础上,子类及左.右父类都各自定义了自己的虚函数,这时的情况又会怎样. struct C140 : public virtual C041 {   ...

  7. 潘凯:C++对象布局及多态实现的探索(八)

    普通的虚继承 下面我们来看虚继承.首先看看这C020类,它从C010虚继承: struct C010 {     C010() : c_(0x01) {}     void foo() { c_ = ...

  8. 潘凯:C++对象布局及多态实现的探索(七)

    构造函数中的虚成员函数调用 在构造函数中调用虚成员函数,虽然这是个不很常用的技术,但研究一下可以加深对虚函数机制及对象构造过程的理解.这个问题也和一般直观上的认识有所差异.先看看下面的两个类定义. s ...

  9. 潘凯:C++对象布局及多态实现的探索(十二)

    后记 结合前面的讨论,我们可以看到,只要牵涉到了虚继承,在访问父类的成员变量时生成的代码相当的低效,需要通过很多间接的计算来定位成员变量的地址.在指针类型转换,动态转型,及虚函数调用时,也需要生成很多 ...

最新文章

  1. esxi所连交换机划vlan导致vm不能通讯
  2. UVa 11107 (后缀数组 二分) Life Forms
  3. C/C++中输入带空格的字符串 string 转 char*
  4. 一行代码取出HTML页面某个按钮的css属性,比如margin
  5. 帝国cms模板仿古筝培训网站
  6. 【AI视野·今日Robot 机器人论文速览 第十期】Fri, 18 Jun 2021
  7. php整合proxool,java discuz的开发笔记-模板代码转换
  8. Unofficial Windows Binaries for Python Extensi...
  9. 2012 年上半年系统分析师 案例分析真题
  10. NODE.JS菜鸟网总结
  11. H3C 重置cons 密码,清空配置
  12. 出现找不到sct文件解决方法:.\Objects\MyFirstExample.axf: error: L6031U: Could not open scatter descript
  13. html5教程源码使用方法,HTML初级教程(一)所有的代码和操作方法
  14. 小豆社保「社保代缴」短信接口被盗刷解决方案-企业短信防火墙
  15. sed 技巧一例:特定位置插入
  16. DHTMLX基本用法
  17. 极简html4网页布局,浅淡极简的网页设计:少即多
  18. 函数的单调性和曲线的凹凸性
  19. 新员工从公司的git上下载代码步骤:
  20. Linux下将swf文件用浏览器读取打开(html、html5嵌入swf格式文件)

热门文章

  1. Traffic (难度不够,题意来凑?)
  2. BurnInTest使用说明
  3. 检错纠错理论——海明码与海明距离
  4. 川西红叶:谁能抵挡如此色诱
  5. 紫光展锐为Open RF Association增添RFIC专长
  6. Ghost软件使用详解
  7. 1347750-74-6,Cbz-N-amido-PEG5-acid含有Cbz保护的氨基和末端羧酸
  8. 创投基金黑钻资本Black3Lab Capital主投互联网3.0
  9. 论文笔记之---Speed and accuracy trade-offs for modern convolutional object detectors
  10. 自媒体账号如何注册?手把手教你只需四步