C++ 虚继承 sizeof大小问题求解

帅气小小少 2014-10-09 12:10:10
如下代码:

class A
{
private :
int a;
public :
virtual int getData();
};

class B : public virtual A
{
private :
int b; //-----------------------------------------(1)
public :
virtual void bb();
};

class C : public virtual B
{
public :
virtual void cc();
};

int main(int argc, char* argv[])
{
cout<<sizeof(A)<<endl; // 8
cout<<sizeof(B)<<endl; // 16
cout<<sizeof(C)<<endl; // 20
return 0;
}

输出结果为8, 16, 20。 这个按照我的理解,答案没问题。

但是,当我把第12行 注释掉以后
 //int b;       //-----------------------------------------(1)

发现,输出结果为8, 12, 12

为什么C类的大小变成12了? 不应该是sizeof(B) + 虚类指针 = 16 么?
...全文
387 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
帅气小小少 2014-10-15
  • 打赏
  • 举报
回复
其实,看VC编译结果我是能理解的。下面是vc按照4字节对齐的结果:

class A size(8):
 +---
 0 | {vfptr}
 4 | a
 +---

class B size(16):
 +---
 0 | {vfptr}
 4 | {vbptr}
 +---
 +--- (virtual base A)
 8 | {vfptr}
12 | a
 +---

class C size(24):
 +---
 0 | {vfptr}
 4 | {vbptr}
 +---
 +--- (virtual base A)
 8 | {vfptr}
12 | a
 +---
 +--- (virtual base B)
16 | {vfptr}
20 | {vbptr}
 +---
但是,我不明白的是gcc是怎么实现这个的? 跟vc有很大不同。下面是gcc结果:

Class A
   size=8 align=4
   base size=8 base align=4
A (0xb74835a0) 0
    vptr=((& A::_ZTV1A) + 8u)

Class B
   size=12 align=4
   base size=4 base align=4
B (0xb6b2d700) 0 nearly-empty
    vptridx=0u vptr=((& B::_ZTV1B) + 12u)
  A (0xb6a854ec) 4 virtual
      vptridx=4u vbaseoffset=-0x00000000c vptr=((& B::_ZTV1B) + 28u)

Class C
   size=12 align=4
   base size=4 base align=4
C (0xb6b2d780) 0 nearly-empty
    vptridx=0u vptr=((& C::_ZTV1C) + 20u)
  B (0xb6b2d7c0) 0 nearly-empty virtual
      primary-for C (0xb6b2d780)
      subvttidx=12u vptridx=4u vbaseoffset=-0x000000014
    A (0xb6a85528) 4 virtual
        vptridx=8u vbaseoffset=-0x00000000c vptr=((& C::_ZTV1C) + 40u)
lucifer886 2014-10-13
  • 打赏
  • 举报
回复
引用 10 楼 slx_391987 的回复:
[quote=引用 8 楼 lucifer886 的回复:] [quote=引用 6 楼 slx_391987 的回复:] [quote=引用 4 楼 wenrenhua08 的回复:] 如果以4字节对齐是12个字节,3张虚函数表。
虚函数表只是一个指针指过去吧,怎么会有3张? 那sizeof(B)=12 又怎么解释?[/quote] 虚继承的时候出了常规的 成员变量长度+虚函数表的指针,还会有一个指向虚指针用来处理菱形问题的基类(不同编译器好像还不太一样,至少g++目前看起来是的) 所以你sizeof(B) = sizeof(A) (虚函数表指针+int a = 8) + A的虚指针 (= 4). 你要是普通继承B的大小就是8[/quote] 我知道 虚继承需要一个指向基类的虚指针。 现在的问题是: 为什么sizeof(C) 不是 sizeof(B) + 指向B的虚指针 = 12 +4 =16 ? [/quote] 如果我没搞错(以前书上看到的),虚拟继承的时候,那个虚基类指针也是一个表(VBTBL),跟虚函数表类似的原理。 可以写个程序测试下(或者用VC看)。
wenrenhua08 2014-10-13
  • 打赏
  • 举报
回复
请看如一篇博文,应该能介你的疑问《C++虚函数表分析》: http://blog.csdn.net/wenrenhua08/article/details/40043811
EDDGA 2014-10-11
  • 打赏
  • 举报
回复
按照#5 vc6的编译,B layout是vfptr+vbptr+b+alignment+A,gcc的B layout是不是不同的,为什么B size是16?
帅气小小少 2014-10-11
  • 打赏
  • 举报
回复
引用 4 楼 wenrenhua08 的回复:
如果以4字节对齐是12个字节,3张虚函数表。
虚函数表只是一个指针指过去吧,怎么会有3张? 那sizeof(B)=12 又怎么解释?
帅气小小少 2014-10-11
  • 打赏
  • 举报
回复
引用 8 楼 lucifer886 的回复:
[quote=引用 6 楼 slx_391987 的回复:] [quote=引用 4 楼 wenrenhua08 的回复:] 如果以4字节对齐是12个字节,3张虚函数表。
虚函数表只是一个指针指过去吧,怎么会有3张? 那sizeof(B)=12 又怎么解释?[/quote] 虚继承的时候出了常规的 成员变量长度+虚函数表的指针,还会有一个指向虚指针用来处理菱形问题的基类(不同编译器好像还不太一样,至少g++目前看起来是的) 所以你sizeof(B) = sizeof(A) (虚函数表指针+int a = 8) + A的虚指针 (= 4). 你要是普通继承B的大小就是8[/quote] 我知道 虚继承需要一个指向基类的虚指针。 现在的问题是: 为什么sizeof(C) 不是 sizeof(B) + 指向B的虚指针 = 12 +4 =16 ?
赵4老师 2014-10-11
  • 打赏
  • 举报
回复
《深度探索C++对象模型》 《C++反汇编与逆向分析技术揭秘》
lucifer886 2014-10-11
  • 打赏
  • 举报
回复
引用 6 楼 slx_391987 的回复:
[quote=引用 4 楼 wenrenhua08 的回复:] 如果以4字节对齐是12个字节,3张虚函数表。
虚函数表只是一个指针指过去吧,怎么会有3张? 那sizeof(B)=12 又怎么解释?[/quote] 虚继承的时候出了常规的 成员变量长度+虚函数表的指针,还会有一个指向虚指针用来处理菱形问题的基类(不同编译器好像还不太一样,至少g++目前看起来是的) 所以你sizeof(B) = sizeof(A) (虚函数表指针+int a = 8) + A的虚指针 (= 4). 你要是普通继承B的大小就是8
mujiok2003 2014-10-10
  • 打赏
  • 举报
回复
msvc 加选项d1reportAllClassLayout可以在编译的时候后查类的内存布局
cl /nologo /EHsc /d1reportAllClassLayout test.cpp
class A size(16):
        +---
 0      | {vfptr}
 8      | a
        | <alignment member> (size=4)
        +---

A::$vftable@:
        | &A_meta
        |  0
 0      | &A::getData

A::getData this adjustor: 0


class B size(40):
        +---
 0      | {vfptr}
 8      | {vbptr}
16      | b
        | <alignment member> (size=4)
        +---
        +--- (virtual base A)
24      | {vfptr}
32      | a
        | <alignment member> (size=4)
        +---

B::$vftable@B@:
        | &B_meta
        |  0
 0      | &B::bb

B::$vbtable@:
 0      | -8
 1      | 16 (Bd(B+8)A)

B::$vftable@A@:
        | -24
 0      | &A::getData

B::bb this adjustor: 0

vbi:       class  offset o.vbptr  o.vbte fVtorDisp
               A      24       8       4 0


class C size(56):
        +---
 0      | {vfptr}
 8      | {vbptr}
        +---
        +--- (virtual base A)
16      | {vfptr}
24      | a
        | <alignment member> (size=4)
        +---
        +--- (virtual base B)
32      | {vfptr}
40      | {vbptr}
48      | b
        | <alignment member> (size=4)
        +---

C::$vftable@:
        | &C_meta
        |  0
 0      | &C::cc

C::$vbtable@C@:
 0      | -8
 1      | 8 (Cd(C+8)A)
 2      | 24 (Cd(C+8)B)

C::$vftable@A@:
        | -16
 0      | &A::getData

C::$vftable@B@:
        | -32
 0      | &B::bb

C::$vbtable@B@:
 0      | -8
 1      | -24 (Cd(B+8)A)

C::cc this adjustor: 0

vbi:       class  offset o.vbptr  o.vbte fVtorDisp
               A      16       8       4 0
               B      32       8       8 0

wenrenhua08 2014-10-10
  • 打赏
  • 举报
回复
如果以4字节对齐是12个字节,3张虚函数表。
帅气小小少 2014-10-09
  • 打赏
  • 举报
回复
引用 2 楼 xpdavis 的回复:
楼主什么编译环境? 我在VC6下面编译,分别是8,20,28和8,16,24
gcc编译。 vc好像默认是8字节对齐,你在vc下加上#pragma pack(4)
铖邑 2014-10-09
  • 打赏
  • 举报
回复
楼主什么编译环境? 我在VC6下面编译,分别是8,20,28和8,16,24
勤奋的小游侠 2014-10-09
  • 打赏
  • 举报
回复
你没有考虑内存对齐,B是16已经是虚大了。

64,683

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧