大家好,我是HuangJacky,技术交流. 在上一文中我说到了一个问题,后来也发现出现问题的地方在那里,但最后由于太晚了,就没有仔细说明其中的原因.好的现在我们接着说原因. 有时间的朋友可以先看看我的另外一篇文章Delphi - 对象构造浅析后续.
首先我们来对象A的内存空间: 我们以4个字节为一个单位来看看 ,每个都是什么意思. 首先看$004BD47C地址: 从旁边的字符,我们可以看到这个是TDerive的定义. 现在看$00401C94是什么,中间那4个0我们先不管. 跳过来是3个地址.现在我们在代码编辑器里面看看这3个地址都是什么代码吧. 这3个地址原来是TDerive中实现IInterface的方法的地址.因为TDerive是从TBase继承下来,而TBase是从TInterfacedObject继承下来,整个继承过程中没有对IInterface的3个三个方法进行任何修改,所以我们这里看见上面图片中也是直接跳到TInterfacedObject中去. 接下来看看$004BD2D0地址上的内容. 这里有四个地址可疑,我们同样在代码编辑器里面查看: 看见没有 这是个地址是IBase接口对应的四个方法.你可能要问 我们IBase接口不是只有个TalkBase方法么?但是你不要忘了IBase是默认继承IInterface的.所以3+1=4啦. 接下来的$00000037 = 55;这个就是FTest的内存区域,一个整型. 下面再看看$004BD3EC的内容: 现在有5个地址可疑,聪明的朋友肯定已经猜到了这个就是IDerive接口实现方法的指针列表.1+1+3=5嘛.不过我们还是来看看: OK,我们猜测正确了.好的,我们现在就可以划分了. 我们可以看到整个数据排列的过程就和继承的过程的一样的.显示类名->继承来自TInterfacedObject中的数据字段(这里只有FRefCount: Integer)->实现IInterface接口的方法->继承来自TBase中的数据字段(没有定义任何字段)->实现IBase接口的方法->TDerive定义的数据字段->实现IDerive接口的方法->$00 00 00 00结尾. 有了上面的结论我们就可以知道,编译器去A.FTest的数据的过程,首先取A的指向地址,也就是对象A的实际内存的起始位置,然后偏移16个字节的位置来取一个PInteger^当成FTest的值. 那么接下来我们要看看Add进去的地址究竟是多少. 也就是上图中我用红色箭头画的IBase处,当我们使用错误的时候 1: A:= TDerive(FList[0]);
2: ShowMessage(IntToStr(A.FTest));
就会出现在$AD2A2C的地址上偏移16个字节,我们看看内存上这个位置是多少?
是$AD2680 = 11347584.我们看看 是不是这么多.
哈哈,和我找的是一样的.
而正确的方法
1: A:= TDerive(IBase(FList[0]));
TDerive(IBase 这里会计算出IBase和TDerive起始的偏移 = -12个字节.
好的问题解释清楚了.是我们存进去的时候指针就是偏移后的,所以取出来用的时候我们需要先反向偏移相应的地址再使用.
谢谢大家,我是HuangJacky.洗脚了.
|
请发表评论