==============================VMT===================================
在创建一个类的实例之后,编译器在该对象的内存空间的首4个字节安插一个指针,该指针指向的地址称为VMT(Virtual Method Table,虚方法表),这个表中放了该类的所有虚方法的入口地址。
在Object Pascal中,所有类实例都会有这么一个指向VMT的指针。如果没有在类中声明虚方法,则该指针为nil。
没有被派生类赋给的方法,编译器会将基类的该方法实现的入口地址填入派生类的VMT中。
其实“指向VMT的指针”所指向的VMT,其实只是真正VMT的一部分,也就是用户定义的第一个虚方法的位置。如果以这个位置为原点,向正方向即刚才所说的VMT,而向负方向,则是语言定义的另一些信息所在地址,析构函数地址就被放在了负方向上了。所以VMT中也有析构函数,但没有出现在我们所能见到的VMT中。这样做的目的,是为了与C++以及COM的VMT相兼容。
据观察:负方向包括7个TObject的虚函数(包括析构函数,位置-4),以及指向其它表格的指针(虚方法表, 接口表, Automation information table, 实例初始化表, 类型信息表,属性表, 方法表, DMT(动态方法表), 类名, 实例大小,指向祖先类指针,安全异常指针)
==============================DMT====================================
VMT的-48处是一个指向DMT的指针,它与VMT有什么关系?
派生类的虚方法表完全继承了基类的虚方法表,只是将被覆盖的虚方法地址变了。基类和每个派生类都有一份自己的虚方法表。可以想象,随着类层次的扩展,虚方法将耗费非常大的内存空间。为了防止这种情况,Object Pascal引入了“dynamic”的概念。dynamic和virtual方法实现相同的功能,只有声明的关键字不同。被声明为dynamic的方法,其入口地址将被放在DMT中。
VMT和DMT的区别在于:对于派生类没有覆盖的方法,这些方法的入口地址不会出现在DMT中,编译器要通过基类的信息来寻找它们的入口地址。
DMT中不会出现没有被派生类覆盖的基类dynamic方法,因此DMT会比VMT节省空间,但是DMT中对基类的动态方法的寻址不是直接进行的,因此dynamic要比virtual要慢一些。当基类有许多虚方法,而派生类只覆盖很少几个时,区别尤其明显。当派生层次越来越深,派生数量越来越多时,DMT就能节省更多的内存空间。virtual与dynamic的区别仅在于编译器采用不同的晚绑定策略而已,对于程序员而言,它们的功能相同。
几乎每个派生类都要覆盖的方法,将它声明为virtual;
如果层次很深,或者派生类很多,但某个方法只被很少的派生类赋给,则声明为dynamic。
另外需要注意的是,只有VMT才与C++、COM的vtable兼容。如果需要这样的兼容性时,只能使用virtual。