一、分析TObject原码

1.1 1.1 看Delphi之祖TObject究竟干了些什么?

1.1.1 1.1.1 类的实例化

    看System单元,TObject类定义实现了几个非常重要的方法。在这里我们只为了搞清楚类、实例和对象在内存中的分配情况,所以我列出这些相关的方法。如:

 class function InitInstance(Instance: Pointer): TObject;

 class function NewInstance: TObject; virtual;

    这两个方法实现了对类的实例化,其中InitInstance方法完成了在内存中开辟一段空间,并对它进行初始化用于存放一个指向vmt表的指针和零个或者多个指向接口表的指针,该接口是指它实现的接口或者他父类实现的接口。它NewInstance负责调用,其参数为一段内存的指针。而NewInstance是当编译时由_ClassCreate负责调用,即是说在类定义后编译器就对类进行实例化了。以下这段代码表明了这一点:

function _ClassCreate(AClass: TClass; Alloc: Boolean): TObject;
asm
{ -> EAX = pointer to VMT }
{ <- EAX = pointer to instance }
PUSH EDX
PUSH ECX
PUSH EBX
TEST DL,DL
JL @@noAlloc
CALL DWORD PTR [EAX] + VMTOFFSET TObject.NewInstance  ......

    当然类实例的释放,就由下面CleanupInstance和FreeInstance两个函数完成了,同理他们的实现原理大家也可以猜到了,这里就不多说了。

1.1.2 1.1.2 对象的初始化

    由于这部分大家都明白,在TObject中提供了这Create、Destroy和Free方法来完成这方面的工作。

1.1.3 1.1.3 vmt表、实例内存段和对象域关系

    经过对TObject类的分析,我们可以得到如图所示的内存分布图: 

    其中,值得注意的是在类实例化的时候不但存放了本身实现的接口的指针,还存放了他父类所实现的接口指针。由于TObject是所有类的祖先,在整个程序运行过程中,内存对vmt表只存在一份,而对相应的类也只存在一份内实例,但是对多个对象就存在多个对象的数据域了。

二. 二、举例说明

2.1 2.1 Demo代码分析

    在Demo实现这样一段代码,我们利用这样一段代码来检查在类实例的域的大小:

TTest = class(TObject)    // 定义一个类
protected
  function _AddRef: Integer; stdcall;
  function _Release: Integer; stdcall;
  function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
end;
I1 = interface          
['{0012DA37-7EDB-4F91-92DB-ED2EC744F7F4}']
end;
I2 = interface
['{640E732C-2413-4BD7-AC9C-B19AC1C384C7}']
end;
TTest1 = class(TTest, I1)   //定义对I1接口的实现类
end;
TTest2 = class(TTest1, I2)  //定义I2接口的实现类,该类继承与TTest1。
end;

    于是,我们利用下面的函数来得到TTest,TTest1和TTest2类的实例域的大小:

procedure TForm1.Button2Click(Sender: TObject);
begin
  ShowMessage('TTest`s InstanceSize is'+Inttostr(TTest.InstanceSize));
  ShowMessage('TTest1`s InstanceSize is'+Inttostr(TTest1.InstanceSize));
  ShowMessage('TTest2`s InstanceSize is'+Inttostr(TTest2.InstanceSize));
end;

    得到的结果为:

  

    可见他们的结果分别是4、8和12字节,为什么是如此呢?那就让我们再看看上面对内存中Instance域究竟存了什么,在内存的Instance域中存放了一个指向vmt表的指针和一些指向接口的指针。由于TTest类没有实现接口,所以它的Instance域只存放了一个指向类vmt的指针大小为4字节;而TTest1实现了接口I1,所以它的Instance域中存放了一个指向它本身类vmt的指针和一个指向接口的指针,即大小为8字节;由于TTest2是继承于TTest1类来实现I2接口的,所以它的Instance域中多存放一个指向父类接口的指针,即大小为12字节。

2.1.1 2.2 Demo中类的Instance域

 

 三、总结

    在Delphi中的TObject中它其实做了很多工作,它给以后派生的类定下了规则,其在内存中各种数据域的组织形式。其实,类的实例的初始化和vmt表的构造是用户不可见的,他在编译的时候就在内存分配了。对我们而言,最重要的是对对象的操作。

posted on 2006-11-16 19:38  望天  阅读(459)  评论(0编辑  收藏  举报