详细介绍c 中的类对象内存模型
创始人
2024-08-01 08:00:24
0

内存模型描述的是程序中各变量(实例域、静态域和数组元素)之间的关系,以及在实际计算机系统中将变量存储到内存和从内存取出变量这样的低层细节.不同平台间的处理器架构将直接影响内存模型的结构.

首先介绍一下C++中有继承关系的类对象内存的布局:

在C++中,如果类中有虚函数,那么它就会有一个虚函数表的指针__vfptr,在类对象最开始的内存数据中。之后是类中的成员变量的内存数据。

对于子类,最开始的内存数据记录着父类对象的拷贝(包括父类虚函数表指针和成员变量)。之后是子类自己的成员变量数据。

对于子类的子类,也是同样的原理。但是无论继承了多少个子类,对象中始终只有一个虚函数表指针。

虚函数表指针

为了探讨C++类对象的内存布局,先来写几个类和函数

首先写一个基类:

  1. class Base   
  2. {   
  3. public:   
  4. virtual void f() { cout << "Base::f" << endl; }   
  5. virtual void g() { cout << "Base::g" << endl; }   
  6. virtual void h() { cout << "Base::h" << endl; }   
  7. int base;   
  8. protected:   
  9. private:   
  10. };  

然后,我们多种不同的继承情况来研究子类的内存对象结构。

1. 无虚函数集继承

  1. //子类1,无虚函数重载   
  2. class Child1 : public Base   
  3. {   
  4. public:   
  5. virtual void f1() { cout << "Child1::f1" << endl; }   
  6. virtual void g1() { cout << "Child1::g1" << endl; }   
  7. virtual void h1() { cout << "Child1::h1" << endl; }   
  8. int child1;   
  9. protected:   
  10. private:   
  11. };   

这个子类Child1没有继承任何一个基类的虚函数,因此它的虚函数表如下图:

Child1的虚函数表

我们可以看出,子类的虚函数表中,先存放基类的虚函数,在存放子类自己的虚函数。

2. 有一个虚函数继承

  1. //子类2,有1个虚函数重载   
  2. class Child2 : public Base   
  3. {   
  4. public:   
  5. virtual void f() { cout << "Child2::f" << endl; }   
  6. virtual void g2() { cout << "Child2::g2" << endl; }   
  7. virtual void h2() { cout << "Child2::h2" << endl; }   
  8. int child2;   
  9. protected:   
  10. private:   
  11. };  

Child2的虚函数表

当子类重载了父类的虚函数,则编译器会将子类虚函数表中对应的父类的虚函数替换成子类的函数。

3. 全部虚函数都继承

  1. //子类3,全部虚函数重载   
  2. class Child3 : public Base   
  3. {   
  4. public:   
  5. virtual void f() { cout << "Child3::f" << endl; }   
  6. virtual void g() { cout << "Child3::g" << endl; }   
  7. virtual void h() { cout << "Child3::h" << endl; }   
  8. protected:   
  9. int x;   
  10. private:   
  11. };  

Child3的虚函数表

#p#

4. 多重继承

多重继承,即类有多个父类,这种情况下的子类的内存结构和单一继承有所不同。

子类的内存结构

我们可以看到,当子类继承了多个父类,那么子类的内存结构是这样的:

子类的内存中,顺序

子类内存的顺序

5. 菱形继承

菱形继承


6. 单一虚拟继承

单一虚拟继承

虚拟继承的子类的内存结构,和普通继承完全不同。虚拟继承的子类,有单独的虚函数表, 另外也单独保存一份父类的虚函数表,两部分之间用一个四个字节的0x00000000来作为分界。子类的内存中,首先是自己的虚函数表,然后是子类的数据成员,然后是0x0,之后就是父类的虚函数表,之后是父类的数据成员。

如果子类没有自己的虚函数,那么子类就不会有虚函数表,但是子类数据和父类数据之间,还是需要0x0来间隔。
因此,在虚拟继承中,子类和父类的数据,是完全间隔的,先存放子类自己的虚函数表和数据,中间以0x分界,最后保存父类的虚函数和数据。如果子类重载了父类的虚函数,那么则将子类内存中父类虚函数表的相应函数替换。

7. 菱形虚拟继承

菱形虚拟继承

结论:

(1) 对于基类,如果有虚函数,那么先存放虚函数表指针,然后存放自己的数据成员;如果没有虚函数,那么直接存放数据成员。

(2) 对于单一继承的类对象,先存放父类的数据拷贝(包括虚函数表指针),然后是本类的数据。

(3) 虚函数表中,先存放父类的虚函数,再存放子类的虚函数

(4) 如果重载了父类的某些虚函数,那么新的虚函数将虚函数表中父类的这些虚函数覆盖。

(5) 对于多重继承,先存放第一个父类的数据拷贝,在存放第二个父类的数据拷贝,一次类推,最后存放自己的数据成员。其中每一个父类拷贝都包含一个虚函数表指针。如果子类重载了某个父类的某个虚函数,那么该将该父类虚函数表的函数覆盖。另外,子类自己的虚函数,存储于第一个父类的虚函数表后边部分。

(6) 当对象的虚函数被调用是,编译器去查询对象的虚函数表,找到该函数,然后调用。

到这c++类对象的内存模型就介绍完了,希望对大家有帮助。

【编辑推荐】

  1. 使用托管C++粘合C#和C++代码(一)
  2. 谈谈C++中三个容易混淆的概念
  3. C/C++是程序员必须掌握的语言吗?
  4. VC++获得当前系统时间的几种方案
  5. C++连接mysql数据库的两种方法

相关内容

热门资讯

如何允许远程连接到MySQL数... [[277004]]【51CTO.com快译】默认情况下,MySQL服务器仅侦听来自localhos...
如何利用交换机和端口设置来管理... 在网络管理中,总是有些人让管理员头疼。下面我们就将介绍一下一个网管员利用交换机以及端口设置等来进行D...
施耐德电气数据中心整体解决方案... 近日,全球能效管理专家施耐德电气正式启动大型体验活动“能效中国行——2012卡车巡展”,作为该活动的...
Windows恶意软件20年“... 在Windows的早期年代,病毒游走于系统之间,偶尔删除文件(但被删除的文件几乎都是可恢复的),并弹...
20个非常棒的扁平设计免费资源 Apple设备的平面图标PSD免费平板UI 平板UI套件24平图标Freen平板UI套件PSD径向平...
德国电信门户网站可实时显示全球... 德国电信周三推出一个门户网站,直观地实时提供其安装在全球各地的传感器网络检测到的网络攻击状况。该网站...
着眼MAC地址,解救无法享受D... 在安装了DHCP服务器的局域网环境中,每一台工作站在上网之前,都要先从DHCP服务器那里享受到地址动...
为啥国人偏爱 Mybatis,... 关于 SQL 和 ORM 的争论,永远都不会终止,我也一直在思考这个问题。昨天又跟群里的小伙伴进行...