详细介绍c++中的类对象内存模型

开发 后端
在C或C++中, 可以利用不同操作平台下的内存模型来编写并发程序.本文详细介绍了C++类对象的内存模型,希望对你有帮助,一起来看。

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

首先介绍一下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数据库的两种方法
责任编辑:于铁 来源: 互联网
相关推荐

2010-01-18 17:48:46

C++类对象

2011-07-20 13:40:09

拷贝构造函数

2011-07-20 15:58:53

C++引用

2010-01-19 18:51:17

C++类

2010-01-25 14:56:08

C++程序

2011-07-15 13:49:30

C++友元函数友元类

2011-07-20 17:16:50

C++重载函数

2010-03-24 12:45:00

Python 嵌入

2011-07-13 16:49:59

C++

2010-01-15 18:35:25

C++的类

2011-07-20 13:57:06

C++STL

2011-07-20 16:48:22

C++static

2011-07-20 16:50:39

inlinec++

2011-07-20 16:57:05

C++const

2011-07-14 16:56:21

2011-07-14 23:27:05

C++引用

2011-07-14 16:26:01

2011-06-21 10:37:56

const

2011-07-20 16:43:34

C++

2010-01-28 11:08:09

C++变量
点赞
收藏

51CTO技术栈公众号