Delphi - 必须的一致的原因

大家好,我是HuangJacky,技术交流.
在上一文中我说到了一个问题,后来也发现出现问题的地方在那里,但最后由于太晚了,就没有仔细说明其中的原因.好的现在我们接着说原因.
有时间的朋友可以先看看我的另外一篇文章Delphi - 对象构造浅析后续.


首先我们来对象A的内存空间:
image
我们以4个字节为一个单位来看看 ,每个都是什么意思.
首先看$004BD47C地址:
image
从旁边的字符,我们可以看到这个是TDerive的定义.
现在看$00401C94是什么,中间那4个0我们先不管.
image 
跳过来是3个地址.现在我们在代码编辑器里面看看这3个地址都是什么代码吧.
image
这3个地址原来是TDerive中实现IInterface的方法的地址.因为TDerive是从TBase继承下来,而TBase是从TInterfacedObject继承下来,整个继承过程中没有对IInterface的3个三个方法进行任何修改,所以我们这里看见上面图片中也是直接跳到TInterfacedObject中去.
接下来看看$004BD2D0地址上的内容.

image

这里有四个地址可疑,我们同样在代码编辑器里面查看:
image
看见没有 这是个地址是IBase接口对应的四个方法.你可能要问 我们IBase接口不是只有个TalkBase方法么?但是你不要忘了IBase是默认继承IInterface的.所以3+1=4啦.
接下来的$00000037 = 55;这个就是FTest的内存区域,一个整型.
下面再看看$004BD3EC的内容:

image 
现在有5个地址可疑,聪明的朋友肯定已经猜到了这个就是IDerive接口实现方法的指针列表.1+1+3=5嘛.不过我们还是来看看:
image
OK,我们猜测正确了.好的,我们现在就可以划分了.
image
我们可以看到整个数据排列的过程就和继承的过程的一样的.显示类名->继承来自TInterfacedObject中的数据字段(这里只有FRefCount: Integer)->实现IInterface接口的方法->继承来自TBase中的数据字段(没有定义任何字段)->实现IBase接口的方法->TDerive定义的数据字段->实现IDerive接口的方法->$00 00 00 00结尾.

有了上面的结论我们就可以知道,编译器去A.FTest的数据的过程,首先取A的指向地址,也就是对象A的实际内存的起始位置,然后偏移16个字节的位置来取一个PInteger^当成FTest的值.
那么接下来我们要看看Add进去的地址究竟是多少.
image
也就是上图中我用红色箭头画的IBase处,当我们使用错误的时候

   1: A:= TDerive(FList[0]);
   2:   ShowMessage(IntToStr(A.FTest));

就会出现在$AD2A2C的地址上偏移16个字节,我们看看内存上这个位置是多少?
image
是$AD2680 = 11347584.我们看看 是不是这么多.
image 哈哈,和我找的是一样的.
而正确的方法

   1: A:= TDerive(IBase(FList[0]));

TDerive(IBase 这里会计算出IBase和TDerive起始的偏移 = -12个字节.


好的问题解释清楚了.是我们存进去的时候指针就是偏移后的,所以取出来用的时候我们需要先反向偏移相应的地址再使用.
谢谢大家,我是HuangJacky.洗脚了.

posted @ 2011-06-17 23:12  HuangJacky  阅读(759)  评论(0编辑  收藏  举报
AdminLogin