五维思考

学习要加,骄傲要减,机会要乘,懒惰要除。 http://www.5dthink.cn

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
 

探索Delphi类与对象的内存结构

                                                                                                        

初次接触DELPHI对它提供的RAD快速编程模式颇感神奇,随手拖放及格控件设定些属性一个应用程序就诞生了,我正是被这种特性所吸引。随着深入,慢慢的窥探到了DELPHIVCL体系,知道了随手拖放背后隐藏的秘密:一切都起源于VCL的对象体系,一切都是面对对象的编程思想。Object pascal就是是怎样实现这个体系的呢,它究竟是如何将面对对象的特性表现出来的呢,Delphi的类和对象究竟是以什么样的形式存在的呢。带着这些问题我翻阅了一些书籍,也借鉴了一些网友的成果,做了下面的探索。

动态内存与静态内存

程序需要执行必须先装载入内存,任何程序表现的数据都存在内存中。当程序运行时,系统首先将所有数据装载入内存,完成初始化,然后从入口地址开始执行代码。程序装载后即存在于内存空间中的数据我们称之为静态内存运行过程中分配的内存我们称之为动态内存。Delphi的类是由编译期间决定的,编译完成后即固定在程序中,所以类是存在于静态内存中对象是由运行期间创建的,所以对象属于动态内存。

image001.gif

注意:后面所提到的TObject均为泛指所有类,而非真正的TObject

                                                        程序运行示意图

类的内存结构

       类的内存结构是固定的,编译完成后就无法改变。它主要存储了类的基本信息,派生对象内存大小,虚方法列表,动态方法列表,公开属性和方法列表(published),接口列表,TObject类的一些方法等等有关于构建对象所必须的信息。这些信息的存储位置在SYSTEM单元中有定义:

vmtSelfPtr           = -76;         指向虚方法表的指针

vmtIntfTable         = -72;          指向接口表的指针

vmtAutoTable         = -68;        指向自动化信息表的指针

vmtInitTable         = -64;          指向实例初始化表的指针

vmtTypeInfo          = -60;        指向类型信息表的指针,这里的数据对于RTTI来说非常重要,它指向一个PTypeInfo类型的指针,有兴趣可以看看TypInfo单元

vmtFieldTable        = -56;          指向域定义表的指针(我开始认为是Published Field,但实际查询时却为NIL

vmtMethodTable       = -52;        指向方法定义表的指针(Published

vmtDynamicTable      = -48;        指向动态方法表的指针

vmtClassName         = -44;       指向类名字符串的指针

vmtInstanceSize      = -40;          对象实例的大小

vmtParent            = -36;        指向父类的指针

vmtSafeCallException = -32 deprecated; 以下都是TOBJECT类的一些虚拟方法指针

vmtAfterConstruction = -28 deprecated;

vmtBeforeDestruction = -24 deprecated;

vmtDispatch          = -20 deprecated;

vmtDefaultHandler    = -16 deprecated;

vmtNewInstance       = -12 deprecated;

vmtFreeInstance      = -8 deprecated;

vmtDestroy           = -4 deprecated;

如果获取对象大小,可以使用以下代码:

Result := PInteger(Integer(TObject) + vmtInstanceSize)^;

其他各项可以依此类推。

l       静态方法

类的静态方法在编译期间就决定了它的地址,类只为所有的派生的对象提供统一的一份静态方法表,不会为每个对象复制一份,所以不必关心静态方法的存储(实际上静态方法也是和动态方法有序的排列在一块的,顺序与方法的实现顺序有关)

l       非静态方法

虚方法(Virtual)和动态方法(Dynamic)均为非静态方法,它们是用来实现面对对象的多态性的关键特性。通过这种特性,开发者可以根据需要在不同的子类中拥有不同的实现,从而使设计变得更加灵活。在具体实现中,编译器只需要将简单的改变表中方法指针的指向即可达到目的。从语法上讲虚拟方法和动态方法是没有任何区别的,凡是声明了该两种类型的方法,在子类中都可以通过override关键字进行覆盖。但实际上二者的实现是存在巨大差别的:

vmtSelfPtr(虚方法表的指针)实际上就是指向TObject位置,所以类的虚拟方法是依次排在TObject所指向的位置之后。

image002.gif

vmtDynamicTable(动态方法表的指针)指向的是动态方法表,动态方法表的结构与虚方法表的结构有所不同。

image003.gif

两者实现方式的不同体现了两者作用的不同。虚拟方法表包括本身以及以上的父类所有的虚拟方法的地址,调用时直接指向地址即可,好处在于速度极快,不需要查询,缺点在于占用了额外的内存。动态方法表则只保存自己本身所包含的动态方法表,如果调用者的动态方法不属于自己,则根据索引号往上级父类遍历查询得到方法的地址,好处在于不用保存父类的动态方法从而节省了内存,缺点在于搜索带来的效率下降。

l       接口表的指针

vmtIntfTable(接口表的指针)指向一块PInterfaceTable类型的接口信息表空间,vmtIntfTable只保存当前类所实现的接口表信息,不保存父类的接口表信息,创建对象时会根据vmtParent父类指针遍历获取所有父类的接口表信息插入对象内存空间。

l       Published Method

vmtMethodTablePublished Method表)指向Published Method表有序排列,只存储当前类的Published Method表,得到父类的Published Method表需要往上遍历。

posted on 2008-10-11 13:32  五维思考  阅读(191)  评论(0编辑  收藏  举报

QQ群:1. 全栈码农【346906288】2. VBA/VSTO【2660245】