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的虚函数表内容