zoukankan      html  css  js  c++  java
  • 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.洗脚了.

  • 相关阅读:
    AOP面向方面编程
    Struts2基于注解的Action配置
    地图api汇总
    Visual C++ 嵌入汇编代码
    C# ASP.net中用到的JWT身份验证
    Asp.Net Forms 身份验证
    .Net 面试常见问题
    Web Api 自动生成帮助文档
    .Net常见的一些区别
    创建Silverlight 5浏览器内受信应用
  • 原文地址:https://www.cnblogs.com/huangjacky/p/2084011.html
Copyright © 2011-2022 走看看