C++类的内存结构

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

C++类的内存结构

⽬录

代码与可执⾏⽂件

代码段,数据段,BSS段,堆、栈

代码段:简单说就是存储函数与常量的地⽅。C/C++写的成员函数,⾮成员函数都是在这⾥。

数据段:初始化的全局变量,初始化的静态变量被编译器放在这⾥

BSS 段: 这⾥存放未初始化的全局变量,未初始化的静态变量。BSS 部分并不占据存储空间,编译器只是把这些为初始化的全局/静态变量记录在这⾥。内存空间要等到执⾏阶段由系统分配,并完成初始化!这也是为什么内存结构上BSS 部分在栈空间下⾯的原因。

堆、栈:这个⽐较好理解,不做介绍了

⼀个⼩c 程序demo

#include <stdio.h>

int global_init = 1;

int global_uinit;

static int global_static_init = 1;

static int global_static_uinit;

int main()

{

int local_init = 1;

int local_uinit;

static int local_static_init = 1;

static int local_static_uinit;

return 0;

}

⽤ nm 查看结果:其中 D、d 都是表⽰ data 段; B、b 表⽰BSS 段。详细介绍参考man nm

C++ 对象与存储

虚指针理解为⼀个 int64_t* 的数组,每个数组成员都是函数指针

先说明⼏点:

空类⼤⼩不为 0

静态类成员函数与类绑定,并不会被注⼊ this 指针

因此静态成员函数可以通过类名直接调⽤,不需要创建类对象。同样因为没有 this 指针,所以静态成员函数也不能调⽤普通成员函数,只能访问静态成员变量。

普通成员函数为类全局共享,不与类实例绑定

但是普通成员函数的调⽤需要绑定实例,这是因为普通成员函数 this 指针的存在; 也因为普通成员函数绑定的不是类实例,所以普通继承关系不具有多态,⽽是由指针决定。

虚表为类全局共享,不与类实例绑定; 虚指针与类实例绑定

虚表是全局存在的,相当于⼀个全局变量,⽽不是每个类实例都创建⼀个虚表,虚表只能通过虚指针来访问。

虚指针在Linux G++/Clang++ 实现是放在类的最前⾯。须指针指向虚表的操作由构造函数初始化。

成员函数不占⽤类的内存空间

即 new ⼀个对象只是创建了对象的数据部分,并不包含函数部分

类的实际内存结构如下:

虚表与虚指针

说明:

1. 虚表指针总是存在在类的头部,并按类的继承顺序排放。⼀个⼦类可以有多个虚表指针,且虚指针个数和具有虚函数的基类个数相

同。

2. 虚成员函数总是按照声明顺序存在于虚表中。

3. 如果存在同名函数,⼦类虚函数会覆盖每⼀个⽗类的每⼀个同名虚函数。

4. ⼦类独有的虚函数填⼊第⼀个虚函数表中,且⽤⽗类指针是不能调⽤。

5. ⽗类独有的虚函数不会被覆盖覆盖。仅⼦类和该⽗类指针能调⽤。

如下图类的内存结构图

⽆虚函数

class Drive

{

public:

void f() {}

};

int main()

{

Drive d;

cout << sizeof(d) << endl;

return 0;

}

如下,类中没有虚函数,只有⼀个成员函数,以及其他默认构造析构函数。类似于空类,类的⼤⼩为1(注意空类⼤⼩不为0,因为为0的话,实例化后没法区分)。因此可以的出结论类的⾮虚成员函数信息不存在于对象实例中!

⽆继承

代码:

class Drive

{

public:

virtual void vf() {}

void f() {}

};

int main()

{

Drive d;

return 0;

}

如下,⼦类经过强制类型转换,得到虚表指针,并提取虚表指针的内容,经过转换可以得到第⼀个虚函数。虚表中只有⼀个虚函数。

单继承

class Base1

{

public:

virtual void vb1f() {}

virtual void vf() {}

};

class Drive : public Base1

{

public:

virtual void vdf() {}

virtual void vf() {}

void f() {}

};

int main()

{

Drive d;

return 0;

}

虚表中只有多个虚函数。顺序是⽗类,⼦类的顺序。其中注意到双⽅共有的虚函数 “vf”,在虚表中⼦类的虚函数覆盖了⽗类的需函数。

多继承

class Base1

{

public:

virtual void vb1f() {}

virtual void vf() {}

};

class Base2

{

public:

virtual void vb2f() {}

virtual void vf() {}

};

class Drive : public Base1, Base2

{

public:

virtual void vdf() {}

virtual void vf() {}

void f() {}

};

int main()

{

Drive d;

return 0;

}

虚表中只有多个虚函数。顺序是⽗类Base1, ⽗类Base2,⼦类。

查看第⼀个虚表:其中注意到双⽅共有的虚函数 “vf”,在虚表中⼦类的虚函数覆盖了⽗类的需函数。另外⼦类的虚函数 ”vdf“ 被放在了第⼀个虚表的后⾯。

查看第⼆个虚表:第⼆个虚表指针在地⼀个虚表指针后⾯。同样⽅式可以看到第⼆个虚表只有⽗类 Base2 的虚成员函数,⽽且共有的虚函数被⼦类的虚函数 vf 覆盖。

虚继承(菱形继承)

单虚继承情况和单继承完全⼀样,这⾥忽略,直接描述虚继承的菱形继承情况

相关文档
最新文档