C++对象模型:编译分析

为了更直观的感受到内存布局,我们使用gcc的编译选项-fdump-lang-class查看

如下代码

class Base{
 public:
  Base(){}
  virtual ~Base(){}
 privte:
  int i;
};

使用如下命令编译

g++ -O0 -std=c++11 -fdump-lang-class test.cpp

可以得到一个显示内存布局的文件a-test.cpp.001l.class

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

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

从Lippman的C++对象模型中我们知道,在含有虚函数的类中,编译器都会生成一个指针,用来指向虚函数表

可知,类中包含一个整型变量i和一个虚函数表指针vptr,由于对齐,Base占用16个字节

虚函数表中包含4个指针,每个大小为8字节
第一个为offset,存储了虚函数表指针的偏移
第二个为typeinfo,指向类型信息对象,用于RTTI
第三个为complete object destructor
第四是deleting destructor

继承下的对象模型

如下代码

class Base{
 public:
  Base(){}
  virtual ~Base(){}
 private:
  int i;
};
class Derive:public Base{
 public:
  Derive(int i){}
  virtual ~Derive(){}
  virtual void get(){}
  virtual void set(){}
 private:
  int j
};
class Single:public Derive{
 public:
  Single(int i){}
  void set()override{}
  virtual void print(){}
 private:
  int k;
};

得到文件

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

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

Vtable for Derive
Derive::_ZTV6Derive: 6 entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI6Derive)
16    (int (*)(...))Derive::~Derive
24    (int (*)(...))Derive::~Derive
32    (int (*)(...))Derive::get
40    (int (*)(...))Derive::set

Class Derive
   size=16 align=8
   base size=16 base align=8
Derive (0x0x7a6689a0e1a0) 0
    vptr=((& Derive::_ZTV6Derive) + 16)
Base (0x0x7a6689b508a0) 0
      primary-for Derive (0x0x7a6689a0e1a0)

Vtable for Single
Single::_ZTV6Single: 7 entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI6Single)
16    (int (*)(...))Single::~Single
24    (int (*)(...))Single::~Single
32    (int (*)(...))Derive::get
40    (int (*)(...))Single::set
48    (int (*)(...))Single::print

Class Single
   size=24 align=8
   base size=20 base align=8
Single (0x0x7a6689a0e208) 0
    vptr=((& Single::_ZTV6Single) + 16)
Derive (0x0x7a6689a0e270) 0
      primary-for Single (0x0x7a6689a0e208)
Base (0x0x7a6689b50d80) 0
        primary-for Derive (0x0x7a6689a0e270)

单继承场景,派生类只有一个虚函数表,复制于基类,同时将override的虚函数进行覆盖,派生类的虚函数也追加到表尾
派生类新增的非静态成员变量,也会追加到基类的成员变量后面
静态成员变量存储在.段,为堆区

多继承下的对象模型

非菱形继承

class Base_A{
 public:
  Base_A(int i){}
  virtual ~Base_A(){}
  int get(){}
  virtual void set(){}
  static void countA(){}
 private:
  int i;
  static int ii;
};

class Base_B{
 public:
  Base_B(int i){}
  virtual ~Base_B(){}
  int get(){}
  virtual void set(){}
  virtual void add(){}
  static void countA(){}
 private:
  int j;
  static int jj;
};

class Derive:public Base_A,public Base_B{
 public:
  Derive(int d){}
  void add()override{}
  void set()override{}
  virtual void print(){}
 private:
  int k;
};

生成如下

Vtable for Base_A
Base_A::_ZTV6Base_A: 5 entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI6Base_A)
16    (int (*)(...))Base_A::~Base_A
24    (int (*)(...))Base_A::~Base_A
32    (int (*)(...))Base_A::set

Class Base_A
   size=16 align=8
   base size=12 base align=8
Base_A (0x0x7983aa950420) 0
    vptr=((& Base_A::_ZTV6Base_A) + 16)

Vtable for Base_B
Base_B::_ZTV6Base_B: 6 entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI6Base_B)
16    (int (*)(...))Base_B::~Base_B
24    (int (*)(...))Base_B::~Base_B
32    (int (*)(...))Base_B::set
40    (int (*)(...))Base_B::add

Class Base_B
   size=16 align=8
   base size=12 base align=8
Base_B (0x0x7983aa9509c0) 0
    vptr=((& Base_B::_ZTV6Base_B) + 16)

Vtable for Derive
Derive::_ZTV6Derive: 13 entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI6Derive)
16    (int (*)(...))Derive::~Derive
24    (int (*)(...))Derive::~Derive
32    (int (*)(...))Derive::set
40    (int (*)(...))Derive::add
48    (int (*)(...))Derive::print
56    (int (*)(...))-16
64    (int (*)(...))(& _ZTI6Derive)
72    (int (*)(...))Derive::_ZThn16_N6DeriveD1Ev
80    (int (*)(...))Derive::_ZThn16_N6DeriveD0Ev
88    (int (*)(...))Derive::_ZThn16_N6Derive3setEv
96    (int (*)(...))Derive::_ZThn16_N6Derive3addEv

Class Derive
   size=32 align=8
   base size=32 base align=8
Derive (0x0x7983aa963930) 0
    vptr=((& Derive::_ZTV6Derive) + 16)
Base_A (0x0x7983aa950f00) 0
      primary-for Derive (0x0x7983aa963930)
Base_B (0x0x7983aa950f60) 16
      vptr=((& Derive::_ZTV6Derive) + 72)

可以看出派生类中存在两个虚函数表指针vptr

enable_shared_from_this特性

表现为让实例拥有一个弱引用

如下代码

class Derive:public Base,
             public std::enable_shared_from_this<Derive{
 public:
  Derive(int i){}
  void set()override;
  virtual void print();
 private:
  int i;
};

可以看出,内存有所增大,因为enable_shared_from_this继承多占用了16字节
从std::weak_ptr实现中可知,实际上存储了两个指针

enable_shared_from_this不影响Derive的虚函数表内容

posted @ 2024-11-03 17:50  sgqmax  阅读(2)  评论(0编辑  收藏  举报