【问题标题】:Why VS compiler insert a NULL into the object memory为什么VS编译器在对象内存中插入一个NULL
【发布时间】:2013-01-06 03:38:19
【问题描述】:
class RootBase{
public:
    RootBase():ai(12){}
    virtual void fas(){
        printf("%p \n", this);
    }
private:
    int ai;

};

class Base : virtual public RootBase{

public:
    Base():bai(1){}

    virtual void fas(){

    }
    virtual void fa(){

    }
private:
    int bai;
};

Base *pBase = new Base; 

我测试了关于类的内存位置的虚拟继承关系案例。结果让我吃惊,因为 Base 的大小是 24 字节。我能理解这20个字节,包括虚函数表指针、虚基表指针和成员变量。

pBase指向的Base对象的内存分布为:

 60 77 41 00 68 77 41 00 01 00 00 00 00 00 00 00 54 77 41 00 0c 00 00 00

但我无法理解第 13-16 个字节的含义,0x00000000,一个 NULL(x86 32 位中的整数 0)。四个字节 0 有什么作用? (操作系统:win xp,IDE:visual studio 8.0)

我也认为整数0代表对齐填充,但是没有理由填充到8字节。

【问题讨论】:

标签: c++ visual-studio


【解决方案1】:

这直接取自您所描述的类的对象布局转储。我希望它能回答你的问题。通过更多研究,我发现vtordisp 用于Base 类。是从使用虚基声明的类的构造函数或析构函数调用虚函数时使用的偏移量。显然是通知他们你的虚函数表在哪里。

来自Jonathan Caves, MSFT,5 年前:

它很少使用 - 但我们必须将它添加到从虚拟基类继承并覆盖虚拟函数的类中,以防用户在构造函数或析构函数中调用虚拟函数。

注意:对于所有行中的1> 序言,我们深表歉意。怪微软,它是他们的工具。

1>  class RootBase  size(8):
1>      +---
1>   0  | {vfptr}
1>   4  | ai
1>      +---
1>  
1>  RootBase::$vftable@:
1>      | &RootBase_meta
1>      |  0
1>   0  | &RootBase::fas
1>  
1>  RootBase::fas this adjustor: 0
1>  
1>  
1>  class Base  size(24):
1>      +---
1>   0  | {vfptr}
1>   4  | {vbptr}
1>   8  | bai
1>      +---
1>  12  | (vtordisp for vbase RootBase)
1>      +--- (virtual base RootBase)
1>  16  | {vfptr}
1>  20  | ai
1>      +---
1>  
1>  Base::$vftable@Base@:
1>      | &Base_meta
1>      |  0
1>   0  | &Base::fa
1>  
1>  Base::$vbtable@:
1>   0  | -4
1>   1  | 12 (Based(Base+4)RootBase)
1>  
1>  Base::$vftable@RootBase@:
1>      | -16
1>   0  | &(vtordisp) Base::fas
1>  
1>  Base::fas this adjustor: 16
1>  Base::fa this adjustor: 0
1>  
1>  vbi    class     offset   o.vbptr  o.vbte fVtorDisp
1>         RootBase      16        4       4          1

采用完全相同相同的代码,但将RootBase设为常规基类(即,不是virtual public RootBase,只是public RootBase)对对象布局:

1>  class RootBase  size(8):
1>      +---
1>   0  | {vfptr}
1>   4  | ai
1>      +---
1>  
1>  RootBase::$vftable@:
1>      | &RootBase_meta
1>      |  0
1>   0  | &RootBase::fas
1>  
1>  RootBase::fas this adjustor: 0
1>  
1>  
1>  class Base  size(12):
1>      +---
1>      | +--- (base class RootBase)
1>   0  | | {vfptr}
1>   4  | | ai
1>      | +---
1>   8  | bai
1>      +---
1>  
1>  Base::$vftable@:
1>      | &Base_meta
1>      |  0
1>   0  | &Base::fas
1>   1  | &Base::fa
1>  
1>  Base::fas this adjustor: 0
1>  Base::fa this adjustor: 0

请注意,随着虚拟基础的消失,不再存储“who-am-i”偏移量,这似乎可以很好地清理对象格式。

【讨论】:

  • 非常感谢!这就是重点!Vtordisp告诉程序,因为派生类不完整,我们只能通过Vtordisp找到基类的虚表,'0'表示指针现在调用的只是基类虚拟表的地址,没有偏移。我的描述对吗?
  • @whoscraig :您的代码 sn-p 显示 vfptr 两次(第一个代码块:0-4 和 16-20)。根据我的理解,类层次结构的 vfptr 仅声明一次。所以由于 RootBase 已经有一个虚拟表指针,它将被 Base 共享。所以,我仍然无法从您的回复中理解,Base 怎么可能是 24 字节,对于 Base 类,总字节数为:RootBase 8 字节 + bai 4 字节 + 虚拟基指针 4 字节 + 4 字节vtordisp.那仍然留下4个字节。如果不用于填充,如何证明这些额外的 4 个字节是合理的?
  • @stamhaney sn-p (实际上是这篇文章中的所有布局层次结构)不是我生成的;它是我的 ms 编译器创建的。从我可以看到一个 vfptr 属于 Base,另一个属于 RootBase。另请注意,如果没有虚拟基类继承,实际上只有一个。我向你保证,我将这个层次结构从我的输出窗口复制到这篇文章是没有错误的。
  • @whoscraig,编译器显示的第二个 vfptr 是继承的一个(来自 RootBase)
猜你喜欢
  • 2022-01-21
  • 2011-09-07
  • 1970-01-01
  • 2010-10-22
  • 2021-08-02
  • 1970-01-01
相关资源
最近更新 更多