searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

C++对象的内存布局

2023-09-19 02:10:35
2
0

单一继承体系下:

在vs2005下查看每一个层次的对象内存布局:

Paraent对象内存布局

Paraent占8个字节,其中m_iParaent占4个字节,虚表指针占4个字节,也就是说虽说成员函数出现在了类的声明中,但实际上是不出现在object中的。首先出现的是虚表指针,然后是按照声明顺序出现的数据成员。

而对于下面的single类的实例,至少占据1个字节,

class single

{

};

Child对象内存布局

Child占据12个字节,其中m_iChild占4个字节,虚表指针是4个字节,m_iParaent是4个字节。

而且只有一个虚表,虚表中Child重载的基类f方法占据了Paraent::f的位置,而其余的位置还是按照Paraent续表的顺序,之后是Child独自拥有的方法的指针。

 

GrandChild的内存布局

GrandChild占16个字节,其中m_iChild占4个字节,虚表指针是4个字节,m_iParaent是4个字节,m_iGrandChild占4个字节。

仍然只有一个虚表,虚表中f的位置替换成了Grang::f,而后是Paraent的方法,而后是Child的方法,而chuild中有被GrangChild重载的方法g_chinld,这个位置被GrangChild::g_child替换掉。

在VS下的测试输出如下:

 

可见以下几个方面:

虚函数表在最前面的位置。

成员变量根据其继承和声明顺序依次放在后面。

在单一的继承中,被overwrite的虚函数在虚函数表中得到了更新。

 

多重继承

 

占据28个字节,其中4个数据成员共16字节,3个虚表指针共12字节。其中Derive和Base1共享的虚表是主要的虚表,f独有的方法指针都是添加到和Base1共享的续表中;f重载的方法,如果在Base1,2中有该方法,则被overwrite的虚函数在虚函数表中也会得到更新。

 

可以看到:

  • 每个父类都有自己的虚表。
  • 子类的成员函数被放到了第一个父类的表中。
  • 内存布局中,其父类布局依次按声明顺序排列。
  • 每个父类的虚表中的f()函数都被overwrite成了子类的f()。这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。

 

 

重复继承

 

D占48字节,10个整型变量占40个字节,两个虚表指针占8个字节。

 

可以看见,顶端的父类B其成员变量存在于B1和B2中,并被D给继承下去了。而在D中,其有B1和B2的实例,于是B的成员在D的实例中存在两份,一份是B1继承而来的,另一份是B2继承而来的。所以,如果我们使用以下语句,则会产生二义性编译错误:

D d;

d.ib = 0; //二义性错误

d.B1::ib = 1; //正确

d.B2::ib = 2; //正确

虽然这种方法消除了二义性的编译错误,但B类在D中还是有两个实例,这种继承造成了数据的重复, C++引入了虚基类的概念避免这个问题。

钻石型多重虚拟继承

这时候的B1的对象的内存布局如下:

B1占据32字节,4个整型变量占16字节,3个虚表占12字节,补齐4个字节.

D的内存占据情况:

 

1>class D size(56):

1> +---

1> | +--- (base class B1)

1> 0 | | {vfptr}

1> 4 | | {vbptr}

1> 8 | | ib1

1>12 | | cb1

1>   | | <alignment member> (size=3)

1> | +---

1> | +--- (base class B2)

1>16 | | {vfptr}

1>20 | | {vbptr}

1>24 | | ib2

1>28 | | cb2

1>   | | <alignment member> (size=3)

1> | +---

1>32 | id

1>36 | cd

1>   | <alignment member> (size=3)

1> +---

1>40 | (vtordisp for vbase B)

1> +--- (virtual base B)

1>44 | {vfptr}

1>48 | ib

1>52 | cb

1>   | <alignment member> (size=3)

1> +---

1>D::$vftable@B1@:

1> | &D_meta

1> |  0

1> 0 | &D::f1

1> 1 | &B1::Bf1

1> 2 | &D::Df

1>D::$vftable@B2@:

1> | -16

1> 0 | &D::f2

1> 1 | &B2::Bf2

1>D::$vbtable@B1@:

1> 0 | -4

1> 1 | 40 (Dd(B1+4)B)

1>D::$vbtable@B2@:

1> 0 | -4

1> 1 | 24 (Dd(B2+4)B)

1>D::$vftable@B@:

1> | -44

1> 0 | &(vtordisp) D::f

1> 1 | &B::Bf

1>D::f this adjustor: 44

1>D::f1 this adjustor: 0

1>D::f2 this adjustor: 16

1>D::Df this adjustor: 0

1>vbi:    class  offset o.vbptr  o.vbte fVtorDisp

1>               B      44       4       4 1

 

0条评论
0 / 1000
唐****茂
2文章数
0粉丝数
唐****茂
2 文章 | 0 粉丝
唐****茂
2文章数
0粉丝数
唐****茂
2 文章 | 0 粉丝
原创

C++对象的内存布局

2023-09-19 02:10:35
2
0

单一继承体系下:

在vs2005下查看每一个层次的对象内存布局:

Paraent对象内存布局

Paraent占8个字节,其中m_iParaent占4个字节,虚表指针占4个字节,也就是说虽说成员函数出现在了类的声明中,但实际上是不出现在object中的。首先出现的是虚表指针,然后是按照声明顺序出现的数据成员。

而对于下面的single类的实例,至少占据1个字节,

class single

{

};

Child对象内存布局

Child占据12个字节,其中m_iChild占4个字节,虚表指针是4个字节,m_iParaent是4个字节。

而且只有一个虚表,虚表中Child重载的基类f方法占据了Paraent::f的位置,而其余的位置还是按照Paraent续表的顺序,之后是Child独自拥有的方法的指针。

 

GrandChild的内存布局

GrandChild占16个字节,其中m_iChild占4个字节,虚表指针是4个字节,m_iParaent是4个字节,m_iGrandChild占4个字节。

仍然只有一个虚表,虚表中f的位置替换成了Grang::f,而后是Paraent的方法,而后是Child的方法,而chuild中有被GrangChild重载的方法g_chinld,这个位置被GrangChild::g_child替换掉。

在VS下的测试输出如下:

 

可见以下几个方面:

虚函数表在最前面的位置。

成员变量根据其继承和声明顺序依次放在后面。

在单一的继承中,被overwrite的虚函数在虚函数表中得到了更新。

 

多重继承

 

占据28个字节,其中4个数据成员共16字节,3个虚表指针共12字节。其中Derive和Base1共享的虚表是主要的虚表,f独有的方法指针都是添加到和Base1共享的续表中;f重载的方法,如果在Base1,2中有该方法,则被overwrite的虚函数在虚函数表中也会得到更新。

 

可以看到:

  • 每个父类都有自己的虚表。
  • 子类的成员函数被放到了第一个父类的表中。
  • 内存布局中,其父类布局依次按声明顺序排列。
  • 每个父类的虚表中的f()函数都被overwrite成了子类的f()。这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。

 

 

重复继承

 

D占48字节,10个整型变量占40个字节,两个虚表指针占8个字节。

 

可以看见,顶端的父类B其成员变量存在于B1和B2中,并被D给继承下去了。而在D中,其有B1和B2的实例,于是B的成员在D的实例中存在两份,一份是B1继承而来的,另一份是B2继承而来的。所以,如果我们使用以下语句,则会产生二义性编译错误:

D d;

d.ib = 0; //二义性错误

d.B1::ib = 1; //正确

d.B2::ib = 2; //正确

虽然这种方法消除了二义性的编译错误,但B类在D中还是有两个实例,这种继承造成了数据的重复, C++引入了虚基类的概念避免这个问题。

钻石型多重虚拟继承

这时候的B1的对象的内存布局如下:

B1占据32字节,4个整型变量占16字节,3个虚表占12字节,补齐4个字节.

D的内存占据情况:

 

1>class D size(56):

1> +---

1> | +--- (base class B1)

1> 0 | | {vfptr}

1> 4 | | {vbptr}

1> 8 | | ib1

1>12 | | cb1

1>   | | <alignment member> (size=3)

1> | +---

1> | +--- (base class B2)

1>16 | | {vfptr}

1>20 | | {vbptr}

1>24 | | ib2

1>28 | | cb2

1>   | | <alignment member> (size=3)

1> | +---

1>32 | id

1>36 | cd

1>   | <alignment member> (size=3)

1> +---

1>40 | (vtordisp for vbase B)

1> +--- (virtual base B)

1>44 | {vfptr}

1>48 | ib

1>52 | cb

1>   | <alignment member> (size=3)

1> +---

1>D::$vftable@B1@:

1> | &D_meta

1> |  0

1> 0 | &D::f1

1> 1 | &B1::Bf1

1> 2 | &D::Df

1>D::$vftable@B2@:

1> | -16

1> 0 | &D::f2

1> 1 | &B2::Bf2

1>D::$vbtable@B1@:

1> 0 | -4

1> 1 | 40 (Dd(B1+4)B)

1>D::$vbtable@B2@:

1> 0 | -4

1> 1 | 24 (Dd(B2+4)B)

1>D::$vftable@B@:

1> | -44

1> 0 | &(vtordisp) D::f

1> 1 | &B::Bf

1>D::f this adjustor: 44

1>D::f1 this adjustor: 0

1>D::f2 this adjustor: 16

1>D::Df this adjustor: 0

1>vbi:    class  offset o.vbptr  o.vbte fVtorDisp

1>               B      44       4       4 1

 

文章来自个人专栏
流媒体开发
2 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0