http://www.cnblogs.com/little-mat/articles/2206627.html

TObject是所有对象的基本类,DELPHI中的任何对象都是一个指针,这个指针指明该对象在内存中所占据的一块空间!   
   对象空间的头4个字节是指向该对象类的虚方法地址表(VMT-Vritual   Method   Table)。接下来的空间就是存储对象本身成员数据的空间,并按从该对象最原始祖先类的数据成员到该对象类的数据成员的总顺序,和每一级类中数据成员的定义顺序存储。   
  类的虚方法地址表(VMT)保存从该类的原始祖先类派生到该类的所有类的虚方法的过程地址。   
  即使,我们自己并未定义任何类的虚方法,但该类的对象仍然存在指向虚方法地址表的指针,只是地址项的长度为零。可是,在TObject中定义的那些虚方法,如Destroy、FreeInstance等等,又存储在什么地方呢?原来,他们的方法地址存储在相对VMT指针负方向偏移的空间中。其实,在VMT表的负方向偏移76个字节的数据空间是对象类的系统数据结构,这些数据结构是与编译器相关的,并且在将来的DELPHI版本中有可能被改变。   
  VMT是一个从负偏移地址空间开始的数据结构,负偏移数据区是VMT的系统数据区,VMT的正偏移数据是用户数据区(自定义的虚方法地址表)。TObject中定义的有关类信息或对象运行时刻信息的函数和过程,一般都与VMT的系统数据有关,事实上,self之上就是RTTI信息。   
  一个VMT数据就代表一个类,其实VMT就是类!在Object   Pascal中我们用TObject、TComponent等等标识符表示类,它们在DELPHI的内部实现为各自的VMT数据。而用class   of保留字定义的类的类型,实际就是指向相关VMT数据的指针。   
  对我们的应用程序来说,VMT数据是静态的数据,当编译器编译完成我们的应用程序之后,这些数据信息已经确定并已初始化。我们编写的程序语句可访问VMT相关的信息,获得诸如对象的尺寸、类名或运行时刻的属性资料等等信息,或者调用虚方法或读取方法的名称与地址等等操作。当一个对象产生时,系统会为该对象分配一块内存空间,并将该对象与相关的类联系起来,于是,在为对象分配的数据空间中的头4个字节,就成为指向类VMT数据的指针。

//创建一个对象   obj   :=   TObject.Create;   
  1)   用   TObject   对应的   VMT   为依据,调用   TObject   的   Create   构造函数。   
  2)   而在   Create   构造函数调用了系统的   _ClassCreate   过程,   
  3)   系统的   ClassCreate   过程又通过类   VMT   调用   NewInstance   虚方法。   
  4)   调用   NewInstance   方法的目的是要建立对象的实例空间,因为我们没有重载该方法,所以,   
        它就是   TObject   类的   NewInstance。   
  5)   TObjec   类的   NewInstance   方法将根据编译器在VMT表中初始化的对象实例尺寸(InstanceSize),   
        调用   GetMem   过程为该对象分配内存,   
  6)   然后调用   InitInstance   方法将分配的空间初始化。   
  7)   InitInstance方法首先将对象空间的头4个字节初始化为指向对象类对应VMT的指针,然后将其余的空间清零。   
  8)   建立对象实例之后,还调用了一个虚方法AfterConstruction。   
  9)   最后,将对象实例数据的地址指针保存到AnObject变量中,这样,obj   对象就诞生了。   
  //消灭一个对象     Obj.Destroy;   
          TObject的析构函数Destroy被声明为虚方法,它也是系统固有的虚方法之一。   
  1)   Destory方法首先调用了   BeforeDestruction   虚方法,   
  2)   然后调用系统的   _ClassDestroy过程。   
  3)   _ClassDestory   过程又通过类VMT调用   FreeInstance   虚方法,   
  4)   由FreeInstance方法调用FreeMem过程释放对象的内存空间。

  就这样,一个对象就在系统中消失。在对象的构造和析构过程中,调用了NewInstance和FreeInstance两个虚函数,来创建和释放对象实例的内存空间。之所以将这两个函数声明为虚函数,是为了能让用户在编写需要用户自己管理内存的特殊对象类时(如在一些特殊的工业控制程序中),有扩展的空间。   
  而将AfterConstruction和BeforeDestruction声明为虚函数,也是为了将来派生的类在产生对象之后,有机会让新诞生的对象呼吸第一口新鲜空气,而在对象消亡之前可以允许对象完成善后事宜,这都是合情合理的事。其实,TForm对象和TDataModule对象的OnCreate事件和OnDestroy事件,就是在TForm和TDataModule重载的这两个虚函数过程分别触发的。   
  TObject的构造函数Create和析构函数Destory竟然没有写任何代码,其实,在调试状态下,通过Debug的CPU窗口,可清楚地反映出Create和Destory的汇编代码。因为,缔造DELPHI的大师门(Hejlsberg...)不想将过多复杂的东西提供给用户,他们希望用户在简单的概念上编写应用程序,将复杂的工作隐藏在系统的内部由他们承担。所以,在发布System.pas单元时特别将这两个函数的代码去掉,让用户认为TObject是万物之源,用户派生的类完全从虚无中开始,这本身并没有错。   
  虽然,阅读DELPHI的这些最本质的代码需要少量的汇编语言知识,但阅读这样的代码,可以让我们更深刻认识DELPHI世界的起源和发展的基本规律。即使看不太懂,能起码了解一些基本东西,对我们编写DELPHI程序也是大有帮助。   
      那是虚拟方法表在内存中的情况,可以清楚的看到   self(此 self 指对象的self)   指针就指向了虚拟方法表的入口,而   self (此 self 指类的self)  指针则位于虚拟方法表的入口地址-76的位置。   
  当然这不具有通用性,因为在将来的delphi版本这个位置可能会变.   
      self之上就是RTTI信息。所以delphi实现RTTI是与C++不同的,C++靠宏。   
  Delphi则依靠编译器,是与delphi当前版本的编译器紧密相关的。   
  不过反正做Pascal编译器的就Borland一家.....

虚拟方法表:$   system.pas   
  {   Virtual   method   table   entries   }   
      vmtSelfPtr                       =   -76;   
      vmtIntfTable                   =   -72;   
      vmtAutoTable                   =   -68;   
      vmtInitTable                   =   -64;   
      vmtTypeInfo                     =   -60;   
      vmtFieldTable                 =   -56;   
      vmtMethodTable               =   -52;   
      vmtDynamicTable             =   -48;   
      vmtClassName                   =   -44;   
      vmtInstanceSize             =   -40;   
      vmtParent               =   -36;   
      vmtSafeCallException   =   -32;   
      vmtAfterConstruction   =   -28;   
      vmtBeforeDestruction   =   -24;   
      vmtDispatch                     =   -20;   
      vmtDefaultHandler         =   -16;   
      vmtNewInstance               =   -12;   
      vmtFreeInstance             =   -8;   
      vmtDestroy                       =   -4;   
      vmtQueryInterface         =   0;   
      vmtAddRef                         =   4;   
      vmtRelease                       =   8;   
      vmtCreateObject             =   12;

}

参考资料:   
  1   <天方夜谈VCL>   
  2   <Delphi的原子世界>

delphi lazarus opengl 网页操作自动化, 图像分析破解,游戏开发

DELPHI 对象的本质 VMT的更多相关文章

  1. Delphi对象变成Windows控件的前世今生(关键是设置句柄和回调函数)goodx

    ----------------------------------------------------------------------第一步,准备工作:预定义一个全局Win控件变量,以及一个精简 ...

  2. Delphi 对象的创建(create)与释放(free/destory)

    Delphi 对象的创建(create)与释放(free/destory) 1.Create参数为:nil/self/application的区别,最好能看到实际效果的区别 例如: My := TMy ...

  3. DELPHI语法基础学习笔记-Windows 句柄、回调函数、函数重载等(Delphi中很少需要直接使用句柄,因为句柄藏在窗体、 位图及其他Delphi 对象的内部)

    函数重载重载的思想很简单:编译器允许你用同一名字定义多个函数或过程,只要它们所带的参数不同.实际上,编译器是通过检测参数来确定需要调用的例程.下面是从VCL 的数学单元(Math Unit)中摘录的一 ...

  4. OC对象的本质及分类

    Object-C的底层都是通过C/C++来实现的,所以OC中的对象也会转化成C/C++中的某一个数据结构, 我们在终端里通过指令 xcrun -sdk iphoneos clang -arch arm ...

  5. iOS进阶一OC对象的本质

    OC对象的本质 平时编写的Object-C代码,底层实现其实都是C/C++代码. 所以Objective-C的面向对象都是基于C/C++的数据结构实现的,OC对象内部可以容纳不同数据类型的数据,因此可 ...

  6. delphi高手突破学习笔记之面向对象类和对象的本质

    知识点1:堆和栈 每个应用程序可以获得的内存空间分为两种:堆(heap)和栈(stack). 堆又称为“自由存储区”,其中的内存空间的分配与释放是必须由程序员来控制的.例如,用GetMem函数获取了一 ...

  7. delphi高手突破学习笔记之面向对象类和对象的本质(有汇编解释 good)

    知识点1:堆和栈 每个应用程序可以获得的内存空间分为两种:堆(heap)和栈(stack). 堆又称为“自由存储区”,其中的内存空间的分配与释放是必须由程序员来控制的.例如,用GetMem函数获取了一 ...

  8. 使用Delphi对象(声明、实例化、构造、释放)

    一.声明和实例化 在使用一个对象之前,用class关键字声明一个对象.可以在一个程序或单元的type部分声明一个对象类型: type TFooObject = class; 除了声明一个对象类型,通常 ...

  9. delphi对象赋值

     Delphi的对象之间赋值主要要注意几个方面的问题:   1.对象之间的 :=赋值只是地址赋值,即是将当前对象的地址赋值到变量中,定义的变量可以是不用初始化的,在内存中两个变量指向的是同一地址空间: ...

随机推荐

  1. (转)Linux tcpdump命令详解

    简介 用简单的话来定义tcpdump,就是:dump the traffic on a network,根据使用者的定义对网络上的数据包进行截获的包分析工具. tcpdump可以将网络中传送的数据包的 ...

  2. linux中 shell编程 判断服务是否运行

    判断nginx是否运行中: if ps -ef|grep "nginx"|egrep -v grep >/dev/null then echo ok! else echo n ...

  3. HBase数据模型和读写原理

    Hbase的数据模型和读写原理: ​ HBase是一个开源可伸缩的分布式数据库,他根据Google Bigtable数据模型构建在hadoop的hdfs存储系统之上. ​ HBase是一个稀疏.多维度 ...

  4. CS229 6.6 Neurons Networks PCA主成分分析

    主成分分析(PCA)是一种经典的降维算法,基于基变换,数据原来位于标准坐标基下,将其投影到前k个最大特征值对应的特征向量所组成的基上,使得数据在新基各个维度有最大的方差,且在新基的各个维度上数据是不相 ...

  5. 《SDN软件定义网络从入门到精通》导论课

    http://mp.weixin.qq.com/s?__biz=MjM5MTM3MzIzMg==&mid=209513316&idx=1&sn=e5dbd9a2ccccb88d ...

  6. Android最新版支付宝支付集成

    上次集成支付宝支付已经很久了,今天写东西用到了支付宝支付,就大致写一下流程: 去蚂蚁金服下载最新版的Android&IOS端SDK 全部文档 -- 资源下载 -- App支付客户端 下载后解压 ...

  7. 关于AJAX与form表单提交数据的格式

    一 form表单传输文件的格式: 只有三种: multipart/form-data 一般用于传输文件,图片文件或者其他的. 那么其中我们默认的是application/x-www-form-urle ...

  8. Linux最大线程数限制及当前线程数查询

    常用配置 echo > /proc/sys/kernel/pid_max a) 当前环境生效 ulimit -d unlimited ulimit -m unlimited ulimit -s ...

  9. MySQL字符集及校对规则的理解

      阅读目录:MySQL的字符集和校对规则 MySQL的字符集 MySQL与字符集 正确使用字符集 MySQL客户端与字符集 字符集编码转换原理 字符集常见处理操作 字符集的正确实践 MySQL的校对 ...

  10. 初学Jmeter添加Http请求,执行接口测试

    最近测试并发,刚开始使用的是录制方法,后面发现录制后无任何界面,加参数也不知从何着手,于是查了很多文章,终于慢慢的着手从http请求来测试并发了. 当然这是个遗留问题,先放在这里后面清楚了再回来补充: ...