C++对象的内存分析(1)
2011-12-12 22:07
309 查看
介绍
虚函数表、虚指针、多态、重写(override)、虚析构、指针调整… 这些概念大家应该都不陌生,不过,除了了解概念和用法,你了解他们背后的实现的机制吗。 本文通过的C++类的对象内存进行分析,来讲解这些面向对象的特性是怎么实现的。本文的目的是为了更好的理解C++面向对象的特性的实质,而对于编译器实 现的细节并不会过多的涉及。本文所指的C++,在没有特别说明时,特指VC++。本文将首先将进行4个课题的研究,分别分析4个C++类的对象内存,最后,讲解构造函数,析构函数和虚析构函数的原理。将分为以下几个章节:
Subject1:一个带虚函数的基类
Subject2:从带虚函数的基类继承的子类
Subject3:从不带虚函数的基类继承的子类
Subject4:多重继承
构造函数与析构函数
Subject1:一个带虚函数的基类。
首先我们来分析一个带虚函数的基类对象的内存布局,厘清一些基本的问题,对于我们接下来分析继承以及多重继承的情况,是很有帮助的。下面是我们要分析的CBasic类:
代码如下:
仔细观察Watch1表中元素地址我们发现:b对象在内存中占12个字节;b对象的第一个元素为4字节长的虚函数指针(因此,虚函数指针地址和对象地址相同),第2个元素为4字节的整型i,第3个元素为4字节的整型指针Array。
我们还发现:虚函数指针指向一个虚函数表(虚函数表并不在b对象内存地址内,每个类对应一个虚函数表),虚函数表是一个函数指针数组,第一个元素指向虚函数add,第二个元素指向虚函数minus。
我们可以画出b对象的内存结构图了:
很显然,在调用b对象的2个虚函数add和minus时,是通过虚函数指针找到虚函数表,再从虚函数表索引到函数的地址的。但是,下面2个问题我们还没有结论:
1)普通成员函数HelloWorld是怎么被调用的呢?在对象的内存和虚表中,我们没有看到任何关于它的信息。
2)CBasic类每个对象都有一个独有的虚函数表吗?还是所有对象共有一个呢?
首先来看第一个问题,看下面的代码:
这和虚函数的调用方式是完全不同的,下面的汇编代码片段再次证明了虚函数是通过虚函数指针和虚函数表来调用的:
SUBJECT1:总结
让我们对一个带虚函数的类的内存布局做一个总结:1)虚函数指针存储在对象内存的最开始4个字节中(这是C++规范决定的)【1】,虚函数指针指向虚表地址。
2)虚函数表是存储函数指针的数组,按照虚函数定义的顺序存储了所有虚函数的实际地址。虚函数被调用时,程序通过虚函数指针索引到虚函数表,再通过虚函数表索引到虚函数的实际地址。虚表并不是对象内存的一部分,类的所有对象共有一个虚函数表【2】.
3)对普通成员函数的调用,编译时就直接编译成对函数地址的直接调用。
4)‘值类型’数据直接存储在对象的内存中;’引用类型’(对象,堆上分配的数组等)只有指向其实际地址的指针存储在对象内存中。
注释
[1]在没有继承或者单继承的情况下,虚函数指针只有一个且始终处于最前端。在多重继承下,可能有好几个虚函数指针,主基类的虚函数指针位于最前端。[2]虚函数表是类关联信息的一部分。
相关文章推荐
- C++对象的内存分析(3)
- C++对象的内存分析(1)
- C++对象的内存分析(2)
- C++对象的内存分析(1)
- C++对象的内存分析(4)
- C++对象的内存分析(5)
- C++对象的内存分析(6)
- C++对象的内存分析(6)
- C++对象的内存分析
- C++对象的内存分析(3)
- C++对象的内存分析(4)
- C++对象的内存分析(4)
- C++对象的内存分析(6)
- C++对象的内存分析(4)
- 将C++对象导出到lua,C++和lua的相互调用
- 一种直接访问C++对象的protected成员的通用方法
- 向ATL DLL中传递C++对象
- C++对象在堆栈区的析构
- c++对象和内存
- 通过VS2010的内存分析工具来分析程序性能问题