【问题标题】:Where does virtual method table store in C++?虚拟方法表在 C++ 中存储在哪里?
【发布时间】:2015-05-05 03:00:33
【问题描述】:

我想知道类对象(不是实例,而是类)如何存储在内存中?

class A {
public:
    int a;
    virtual void f();
    virtual ~A();
};

class B : public A {
public:
    int b;
    void f() final override;
};

我知道,在这种继承(B 派生自 A)的情况下,通常(标准没有强烈描述)我们有:

memory: ....AB...

其中 AB 是 B 的类对象(如果我理解正确的话)。 如果我们更深入(尝试使用 clanggcc),我们可以看到类似的东西(同样,标准中没有强烈描述):

A
    vtptr*
    int a
B
    vtptr*
    int b

好的,现在我们看看ab 属性存储在哪里。我们还看到了指向虚拟方法表的指针。但是vtptr*(虚拟方法表)实际存储在哪里?为什么不靠近上课?还是有?

另外,这是另一个问题:我能够通过更改指针来更改虚拟方法表(简单逻辑)。我也可以安全地更改指向它的方法的指针吗?

附:在您的问题中,您可以回答 gcc 和 clang。 附言如果我在某个地方错了,请在您的答案中指出。

【问题讨论】:

  • 虽然您可能会发现类似的实现,但这取决于实现。您必须指定编译器才能获得合理的答案。
  • vtables 不存储在对象中,因为它在内存方面效率非常低。但是,标准中没有任何内容会阻止实现这样做。我理解你的好奇心,但只要你编写的是健全的代码,你应该很少(如果有的话)担心实现细节。此外,弄乱 vtable 或 vtptrs(以及做任何没有被标准严格定义的事情)永远不会被认为是安全的。
  • 如果你看看g++ -fdump-class-hierarchy -c filename.cpp的结果,也许你会明白一点
  • 在某些地方你使用类来表示实例(例如为什么不靠近类)。这对我来说很奇怪,因为您在第一句话中专门从类中描述了实例。

标签: c++ memory virtual


【解决方案1】:

C++ 标准没有规定应该如何实现虚函数机制。在实践中,所有 C++ 实现对每个类都使用一个虚函数表,并在每个具有虚函数的类对象中使用一个虚函数表指针(称为多态类)。然而,细节可能有所不同,特别是对于多重继承和虚拟继承。

您可以在 Stanley Lippman 的经典著作Inside The C++ Object Model 中阅读常见选择。

询问虚函数表存储在“哪里”没有多大意义。它很像任何静态变量:它的位置取决于实现并且几乎是任意的。而关于

为什么不去上课呢?

...类本身并没有存储在任何地方,它们不是对象,所以这没有意义,抱歉。

对于给定的实现,您可以更有意义地询问每个对象中存储的 vtable 指针在哪里?

通常这是在对象的开头,但是如果您从非多态类派生并添加一个虚函数,那么您可能会在其他地方获得 vtable 指针。或不。后一种可能性是static_castDerived*Base*(反之亦然)可以进行地址调整的主要原因,即不同于简单的reinterpret_cast

【讨论】:

    【解决方案2】:

    阅读virtual method table 上的维基页面。

    vtable(本身)存储在哪里是特定于实现的(编译器、链接器、操作系统特定)。但它通常存储在可执行文件的code segment 中(就像文字字符串一样)。所以对象通常(即没有多重继承)以指向它们的 vtable 的 _vptr 指针开头。通过多重或虚拟继承,您可以拥有 几个 vtable 指针。

    正如评论,您不应该关心这些细节。如果您真的很在意,请让您的编译器转储内部表示或发出的汇编代码。 (例如用g++ -fdump-tree-all -fverbose-asm -S编译)

    【讨论】:

      【解决方案3】:

      但是 vtptr*(虚拟方法表)实际上 [point] 在哪里?为什么不靠近上课?还是有?

      它可能在任何地方......谁在乎呢?它的工作方式通常是这样实现的……想象一下class A 有一个隐藏的static 成员:

      VDT A::vdt = {
          { address of A::f code, 
            address of A::~A code },
          miscellaneous type-specific information needed for dynamic cast etc.
      };
      

      确切的布局未知,但很可能有virtual 成员函数的地址数组。与任何static 信息一样,地址与任何给定对象实例的地址无关……对象中指向虚拟调度表的指针允许这种解耦。

      另外,还有一个问题:我能够通过更改指针来更改虚拟方法表(简单逻辑)。我也可以安全地更改指向它的方法的指针吗?

      这是不安全的,即使它表面上可以工作,有时也可能无法始终如一地兑现(例如,在编译器能够确定要在编译时调用的特定覆盖的情况下,它可能会完全绕过运行时虚拟调度表咨询)。

      【讨论】:

        猜你喜欢
        • 2016-06-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-10
        相关资源
        最近更新 更多