C++虚函数表深度剖析

使用命令

g++ g++ -fdump-lang-class main.cpp
VScl /d1 reportAllClassLayout main.cpp

以下以g++环境进行分析

单继承

class Base
{
public:
    Base(int a = 1, int b = 2){};
    virtual void fun1();
    virtual void func2(){};

private:
    int a = 1;
    int b = 1;
};

class Derive : public Base
{
public:
    Derive(int a = 1, int b = 2) : Base(a, b){};

private:
    int a = 1;
    int b = 1;
};

 通过实践 g++ -fdump-lang-class main.cpp,如图注释,vptr确切翻译应该为虚指针, 指向第一个虚函数地址而不是虚函数表地址。vptr指向的是虚函数表的首地址地址+16的位置,这不正好是func的地址么。

Vtable for Base  //Base的虚函数表
Base::_ZTV4Base: 4 entries
0     (int (*)(...))0              //top_offset多继承情况下使用
8     (int (*)(...))(& _ZTI4Base)  //RTTI类型,typeid(), dynamic_cast使用的就是这个
16    (int (*)(...))Base::fun1
24    (int (*)(...))Base::func2

Class Base
   size=16 align=8
   base size=16 base align=8
Base (0x0x7f04d7259420) 0
    vptr=((& Base::_ZTV4Base) + 16)

Vtable for Derive   //Derive的虚函数表
Derive::_ZTV6Derive: 4 entries
0     (int (*)(...))0                 //top_offset多继承情况下使用
8     (int (*)(...))(& _ZTI6Derive)   //RTTI类型,typeid(), dynamic_cast使用的就是这个
16    (int (*)(...))Base::fun1
24    (int (*)(...))Base::func2

Class Derive
   size=24 align=8             //对齐后的总大小,内存对齐大小
   base size=24 base align=8   //没有对齐的情况下的大小
Derive (0x0x7f04d71051a0) 0
    vptr=((& Derive::_ZTV6Derive) + 16)  //虚函数指针,指向Derive虚函数表的第一个虚函数
  Base (0x0x7f04d7259660) 0
      primary-for Derive (0x0x7f04d71051a0)

多继承

class Base
{
public:
    Base(int a = 1, int b = 2){};
    virtual void fun1();
    virtual void func2(){};

private:
    int a = 1;
    int b = 1;
};


class Base1
{
public:
    Base1(int a = 1, int b = 2){};
    virtual void fun3();
    virtual void func4(){};

private:
    int a = 1;
    int b = 1;
};

class Base2
{
public:
    Base2(int a = 1, int b = 2){};
    virtual void fun3();
    virtual void func4(){};

private:
};

class Derive : public Base, Base1, Base2
{
public:
    Derive(int a = 1, int b = 2) : Base(a, b), Base1(a, b), Base2(a, b){};
private:
    int a = 1;
    int b = 1;
};

 通过 g++ -fdump-lang-class main.cpp, 结果如图,多继承的情况下的内存布局,这里我使用的是64位服务器,所以vptr是8字节,内存对齐也是按8字节对齐。

内存布局和虚函数表的top-offset偏移量可以看出,内存是先存储第一直接基类的vptr(覆盖后)和成员变量,然后是以此是第二和第三基类的vptr和成员变量,这里的虚函数表就填入了top-offset,他就是Base1:vptr的偏移量等等。

Vtable for Base
Base::_ZTV4Base: 4 entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI4Base)
16    (int (*)(...))Base::fun1
24    (int (*)(...))Base::func2

Class Base
   size=16 align=8
   base size=16 base align=8
Base (0x0x7fc61fd92420) 0
    vptr=((& Base::_ZTV4Base) + 16)

Vtable for Base1
Base1::_ZTV5Base1: 4 entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI5Base1)
16    (int (*)(...))Base1::fun3
24    (int (*)(...))Base1::func4

Class Base1
   size=16 align=8
   base size=16 base align=8
Base1 (0x0x7fc61fd92660) 0
    vptr=((& Base1::_ZTV5Base1) + 16)

Vtable for Base2
Base2::_ZTV5Base2: 4 entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI5Base2)
16    (int (*)(...))Base2::fun3
24    (int (*)(...))Base2::func4

Class Base2
   size=8 align=8
   base size=8 base align=8
Base2 (0x0x7fc61fd928a0) 0 nearly-empty
    vptr=((& Base2::_ZTV5Base2) + 16)

Vtable for Derive
Derive::_ZTV6Derive: 12 entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI6Derive)
16    (int (*)(...))Base::fun1
24    (int (*)(...))Base::func2
32    (int (*)(...))-16
40    (int (*)(...))(& _ZTI6Derive)
48    (int (*)(...))Base1::fun3
56    (int (*)(...))Base1::func4
64    (int (*)(...))-32
72    (int (*)(...))(& _ZTI6Derive)
80    (int (*)(...))Base2::fun3
88    (int (*)(...))Base2::func4

Class Derive
   size=48 align=8                        //48可以看出是有三个vptr
   base size=48 base align=8
Derive (0x0x7fc61fdaa348) 0
    vptr=((& Derive::_ZTV6Derive) + 16)   //第一个虚指针
  Base (0x0x7fc61fd92ae0) 0
      primary-for Derive (0x0x7fc61fdaa348)
  Base1 (0x0x7fc61fd92b40) 16             //这里的16对象内存偏移量
      vptr=((& Derive::_ZTV6Derive) + 48)  //第二个虚指针
  Base2 (0x0x7fc61fd92ba0) 32 nearly-empty  //这里的32对象内存偏移量
      vptr=((& Derive::_ZTV6Derive) + 80)  //第三个个虚指针

 

引入虚继承,菱形继承

突然发现这个博主已经有总结的了,且总结的内容跟我实验的过程差不多,就不写重复工作了。

C++在gcc下的单继承,多继承,虚继承的内存布局_LupinLeo的博客-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

YanWenCheng_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值