虚方法表在C ++中存储在哪里?

时间:2015-03-04 07:52:19

标签: c++ memory virtual

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

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的类对象(如果我理解正确的话)。 如果我们更深入(尝试 clang gcc ),我们可以看到类似的东西(再次,标准中没有强烈描述):

A
    vtptr*
    int a
B
    vtptr*
    int b

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

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

P.S。在你的问题中,你可以回答gcc和clang。 P.P.S.如果我在某处错了,请在答案中指出。

3 个答案:

答案 0 :(得分:2)

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

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

询问存储虚拟功能表的“位置”没有多大意义。它与任何静态变量非常相似:它的位置取决于实现,并且非常随意。关于

  

为什么不靠近课程?

......这样的课程没有存储在任何地方,它们不是对象,所以这没有意义,抱歉。

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

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

答案 1 :(得分:1)

阅读virtual method table上的wikipage。

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

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

答案 2 :(得分:0)

  

但是vtptr *(虚拟方法表)实际上在哪里[指向]?为什么不靠近课程?或者确实如此?

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

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

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

  

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

这是不安全的,即使它表面上有效也可能无法保持一致(例如,在编译器能够确定在编译时调用的特定覆盖的情况下,它可能绕过运行时虚拟发货表咨询完全)。