【问题标题】:What data does a TObject contain?TObject 包含哪些数据?
【发布时间】:2009-03-24 20:01:34
【问题描述】:

TObject.InstanceSize 返回 8,但 TObject 没有声明任何数据成员。根据 TObject.ClassType 的实现,前 4 个字节可以解释为指向对象的 TClass 元数据的指针。有谁知道另外 4 个字节的开销是干什么用的?

编辑:显然这是特定于 D2009。在旧版本中,它只有 4 个字节。

【问题讨论】:

    标签: delphi object delphi-2009 tobject


    【解决方案1】:

    在 Delphi 2009 中,有the ability to have a reference to a synchronization monitor。见:

    class function TMonitor.GetFieldAddress(AObject: TObject): PPMonitor;
    class function TMonitor.GetMonitor(AObject: TObject): PMonitor;
    

    ...在 System.pas 中

    此外,还有一个指向 VMT 的指针。 (虚拟方法表。)From Delphi in a Nutshell:

    TObject 类声明了几个 方法和一个特殊的隐藏字段 存储对对象的引用 班级。这个隐藏字段指向 类的虚拟方法表 (VMT)。 每个班级都有一个独特的 VMT 和所有 该类的对象共享 班级的 VMT。

    【讨论】:

    • 其实是一样的。一个类的 TClass 引用指向它的 VMT。所以它是相同的4个字节。其他 4 个是什么?
    • (值得注意的是那本书是9年前写的,可能当时只有一个隐藏字段,现在好像有两个。)
    • D2009 中还有一个同步监视器。我会更新的。
    • 哦,那是 D2009 特有的吗?嗯……有趣。如果我没看错的话,看起来 TMonitor 的最后 4 个字节在对象数据之后,而 TClass 指针在它之前。对吗?
    • 看起来像是在 D2009 中添加的:blogs.embarcadero.com/abauer/2008/02/19/38856 查看该帖子中的链接以获取完整的详细信息。
    【解决方案2】:

    一个对象包含其所有字段的条目,加上额外的空间来保存指向虚拟方法表的指针。 VMT 不仅仅包含虚拟方法指针。我在我的网站上解释了more about the VMT,包括一张图表。

    显然,Delphi 2009 除了 VMT 指针之外还引入了另一个隐藏字段来保存同步监视器。可以通过一些简单的代码来判断是加在类的开头还是结尾:

    type
      TTest = class
        FField: Integer;
      end;
    
    var
      obj: TTest;
      ObjAddr, FieldAddr: Cardinal;
    begin
      Assert(TTest.InstanceSize = 12);
      obj := TTest.Create;
      ObjAddr := Cardinal(obj);
      FieldAddr := Cardinal(@(obj.FField));
      writeln(FieldAddr - ObjAddr);
    end.
    

    如果它打印值 4,则监控字段必须位于对象的末尾,因为 4 仅说明 VMT 指针的大小。如果它打印值 8,则监视字段必须位于开头,与 VMT 指针相邻。

    我希望您会在开始时找到监视器。否则,这意味着后代对象的布局不仅仅是附加了所有新字段的基础对象的布局。这意味着监视字段的偏移量取决于对象的运行时类型,这使得实现更加复杂。

    当一个类实现一个接口时,对象布局包括更多的隐藏字段。这些字段包含指向对象的接口引用值的指针。当你有一个对象的 IUnknown 引用时,它所持有的指针与指向对象的 VMT 字段的指针不同,这是普通对象引用所拥有的。 IUnknown 指针值将是隐藏字段的地址。我写了more about the layout of classes that implement interfaces。

    【讨论】:

    • 不。 GetInterfaceTable 是一个基于从 VMT 位置偏移的类函数。看一下 TMonitor.GetFieldAddress 的实现。看起来它取决于运行时类型,就像你描述的那样。
    • GetInterfaceTable 获取不同的东西,与接口引用分开。接口表由类的所有实例共享,就像 VMT 一样。接口引用不被其他实例共享。阅读我的文章,了解有关接口类布局的更多信息。
    • 我无法检查 TMonitor.GetFieldAddress 的来源;我没有 Delphi 2009。
    • 有趣。所以每个接口都添加了另一个隐藏指针。尽管如此,TObject 本身并没有实现任何接口。但这是一个很酷的事实。谢谢!
    • 它不会改变现有的对象布局,因此任何依赖于特定对象布局的东西仍然可以工作。例如,您仍然可以使用类创建 COM 接口,并且需要特定的类实例布局。
    【解决方案3】:

    如果有人想知道为什么 Craig Stuntz 的回答被接受,请参阅他对该回答的最后评论:

    看起来像是在 D2009 中添加的:http://blogs.embarcadero.com/abauer/2008/02/19/38856 查看该帖子中的链接以获取完整的详细信息。

    链接不再可用,但回程机器有它:

    https://web.archive.org/web/20160409224957/blogs.embarcadero.com/abauer/2008/02/19/38856

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-19
      • 1970-01-01
      • 2010-12-26
      • 1970-01-01
      • 2019-11-25
      • 1970-01-01
      相关资源
      最近更新 更多