// ...
function ClassType: TClass;
class function ClassInfo: Pointer;
class function MethodAddress(const Name: ShortString): Pointer;
class function MethodName(Address: Pointer): ShortString;
function FieldAddress(const Name: ShortString): Pointer;
function GetInterface(const IID: TGUID; out Obj): Boolean;
class function GetInterfaceEntry(const IID: TGUID): PInterfaceEntry;
class function GetInterfaceTable: PInterfaceTable;
function SafeCallException(ExceptObject: TObject;
ExceptAddr: Pointer): HResult; virtual;
// ...
end;
四,对象释放服务
凡是声明为 destructor 的方法都属于构造函数,不管你用不用 Destroy 作为方法名称,但建议只用 Destroy 作为方法名称;还有一点要记住:由于在 Destroy 在 TObject 中声明为虚方法,因此派生类如果也声明析构函数的话,别忘了用 override 标记。关于 Destroy 为什么设为虚方法,李维在《Inside VCL》中解释得太差劲了,用了数页的篇幅去分析这个问题,但始终也没有讲到点子上,其实最主要的原因只有一个:析构函数经常被多态调用,而这时对象引用的声明类型可能与对象的实际类会有不同[4]。
对象释放服务其实就是对象创建服务的逆过程,可以认为对象释放服务就是回收对象在创建过程中分配的资源。
当编译器遇到 destructor 关键字通常会这样编码:首先调用 System._BeforeDestruction,而 System._BeforeDestruction 继而调用虚方法 BeforeDestruction, 在 TObject 中 BeforeDestruction 中只是个 Place Holder,你很少需要重载这个方法,重载这个方法通常只是为了与 C Builder 对象模型兼容。
这之后,编译器调用你在 Destroy 中真正写的代码,如果当前你在撰写的类是继承链上的一员,不要忘记通过 inherited 调用父类的析构函数以释放父类分配的资源,但规矩是,先释放当前类的资源,然后再调用父类的,这和对象创建服务中设置对象执行框架的顺序恰好相反。
当前类及继承链中所有类中分配的资源全部释放后,最后执行的就是释放掉对象本身及一些特别数据类型占用的内存空间。编译器调用 System._ClassDestroy 来完成这件工作。System._ClassDestroy 继而调用虚方法 FreeInstance,继承类通常不需要重载 TObject.FreeInstance,除非你使用自己的内存管理器,因此缺省是调用 TObject.FreeInstance。TObject.FreeInstance 继而调用 TObject.CleanupInstance 完成对于字符串数组、宽字符串数组、Variant、未定义类型数组、记录、接口和动态数组这些特别数据类型占用资源的释放[4],最后 TObject.FreeInstance 调用 MemoryManager.FreeMem 释放对象本身占用的内存空间。
很有意思的是,对象释放服务与对象创建服务所用方法、函数是一一对应的,是不是有一种很整齐的感觉?
对象创建服务
对象释放服务
System._ClassCreate
System._ClassDestroy
System._AfterConstruction
System._BeforeDestruction
TObject.AfterConstruction(virtual)
TObject.BeforeDestruction(virtual)
TObject.NewInstance(virtual)
TObject.FreeInstance(virtual)
TObject.InitInstance
TObject.CleanupInstance
MemoryManager.GetMem
MemoryManager.FreeMem
还有一点要注意,通常我们不会直接调用 Destroy 来释放对象,而是调用 TObject.Free,它会在释放对象之前检查对象引用是否为 nil。
相关 TObject 方法:
TObject = class
// ...
procedure Free;
procedure CleanupInstance;
procedure BeforeDestruction; virtual;
procedure FreeInstance; virtual;
destructor Destroy; virtual;
// ...
end;
procedure _ClassDestroy(Instance: TObject);
function _BeforeDestruction(Instance: TObject; OuterMost: ShortInt): TObject;
五,对象消息分派服务
消息分派服务通常认为是跟 Windows 平台紧密结合的,但在 TObject 这一根类中可以认为消息分派与平台无关,因为只要具有 MessageID 的消息 TObject.Dispatch 都可以正确分派,大大扩展了 Windows 对消息的定义,关于 Delphi 消息机制可以阅读我的另一篇文章《Delphi 的消息机制学习笔记》。
相关 TObject 方法:
TObject = class
// ...
procedure Dispatch(var Message); virtual;
procedure DefaultHandler(var Message); virtual;
// ...
end;
六,虚拟方法表格和动态方法表格
Delphi 中虚方法是通过类的虚拟方法表格(Virtul Method Table,简称 VMT)或者动态方法表格(Dynamic Method Table,简称 DMT)实现的。
VMT 中存放着类及其基类声明的所有虚方法的指针[4]。每个类都具有一个唯一的 VMT,并且是在编译期间就确定的,而不象李维在《Inside VCL》中所说是在第一个类对象创建时才会具体的创建在内存中(李维为什么会犯这样的低级错误?),所有由该类创建的对象都共享同一个 VMT。
VMT 中除了一个虚方法表外,还包括其他有关一个类的信息[4]。一个 VMT 能衍生出很多故事,这不是我这篇笔记所有容纳的,关于 VMT的详细内容建议大家配合文章最后所列参考文献进行学习。
DMT 是 Delphi 中实现虚方法的另一种方式。利用 DMT 可以有效减小 VMT 的体积,但执行效率稍差。很少有资料将 DMT 的执行过程解释得很清楚,但你会在我这篇笔记中得到答案,准备好了吗?跟我来吧!
DMT 中存有类所声明的动态方法和消息句柄,但并不包括从祖先类继承的方法。用关键字 dynamic 或者 message 声明的方法都会以动态方法实现。从 VMT vmtDynamicTable 偏移处取出的 DWORD 值就是指向类的 DMT 指针。DMT 的结构在 Delphi 中是 Undocumented 的,但通过阅读 VCL 源码和 Debug 很容易写出 DMT 在内存中的逻辑布局:
type
TDynmethodTable = packed record
Count: Word;
Indexes: packed array[1..Count] of SmallInt;
Addresses: packed array[1..Count] of Pointer;
end;
假如我们声明了如下的类:
文章整理:西部数码--专业提供域名注册、虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!




