C++对象中的VTable布局和VTable指针位置在GCC 3.x和4.x中是什么?

17

我正在寻找VTable结构的细节,顺序和内容,以及对象内vtable指针的位置。

理想情况下,这将涵盖单继承,多重继承和虚拟继承。

也希望提供对外部文档的引用。

GCC 4.0x类布局的文档在这里,Itanium和更广泛的GNU ABI布局文档在这里


4
这可能在C++ ABI中有所描述。例如,C++ VTable例子 - squadette
3
你尝试过使用gcc的'-fdump-class-hierarchy'选项吗? - jaor
前面的链接已经失效了!D: - minmaxavg
2个回答

12

虚拟表通常被视为函数指针数组,尽管编译器也可以将数据指针(在多重继承和虚继承场景中或者用于类型信息) 、整数(用于修复)或哨兵元素(比如NULL指针)放入其中。布局通常是编译器特定的(或者多个C++编译器共享ABI),但只要正在编译的类具有稳定的接口,布局就是稳定的(否则你需要一直重新编译代码,这很麻烦)。还有一些额外的表格需要处理涉及虚拟和多重继承的边缘情况,并确保在派生类构造期间发生虚拟调用时能够按照标准进行操作(这就是下面输出中的VTT和构造表所用之处)。

至于GCC 4.x的具体情况: -fdump-class-hierarchy开关的确像描述的那样(甚至更多)。我使用下面的示例代码在Coliru上测试了它:

struct Base
{
    virtual ~Base() {}
    virtual void f() = 0;
};

struct OtherBase
{
    virtual ~OtherBase() {}
    virtual void g() {}
};

struct Derived: public Base
{
    virtual ~Derived() {}
    virtual void f() {}
};

struct MultiplyDerived: public Base, public OtherBase
{
    virtual ~MultiplyDerived() {}
    virtual void f() {}
    virtual void g() {}
};

struct OtherDerived: public Base
{
    virtual ~OtherDerived() {}
    virtual void f() {}
};

struct DiamondDerived: public Derived, public OtherDerived
{
    virtual ~DiamondDerived() {}
    virtual void f() {}
};

struct VirtuallyDerived: virtual public Base
{
    virtual ~VirtuallyDerived() {}
    virtual void f() {}
};

struct OtherVirtuallyDerived: virtual public Base
{
    virtual ~OtherVirtuallyDerived() {}
    virtual void f() {}
};

struct VirtuallyDiamondDerived: public VirtuallyDerived, public OtherVirtuallyDerived
{
    virtual ~VirtuallyDiamondDerived() {}
    virtual void f() {}
};

struct DoublyVirtuallyDiamondDerived: virtual public VirtuallyDerived, virtual public OtherVirtuallyDerived
{
    virtual ~DoublyVirtuallyDiamondDerived() {}
    virtual void f() {}
};

struct MixedVirtuallyDerived: virtual public Base, public OtherBase
{
    virtual ~MixedVirtuallyDerived() {}
};

struct MixedVirtuallyDiamondDerived: public VirtuallyDerived, public MixedVirtuallyDerived
{
    virtual ~MixedVirtuallyDiamondDerived() {}
    virtual void f() {}
    virtual void g() {}
};

struct VirtuallyMultiplyDerived: virtual public Base, virtual public OtherBase
{
    virtual ~VirtuallyMultiplyDerived() {}
};

struct OtherVirtuallyMultiplyDerived: virtual public Base, virtual public OtherBase
{
    virtual ~OtherVirtuallyMultiplyDerived() {}
};

struct MultiplyVirtuallyDiamondDerived: public VirtuallyMultiplyDerived, public OtherVirtuallyMultiplyDerived
{
    virtual ~MultiplyVirtuallyDiamondDerived() {}
    virtual void f() {}
    virtual void g() {}
};

并且从G++收到(有关名称缩编的指南:TI是类型信息,TV是虚函数表,Th和Tv是在存在多继承和/或虚拟继承时用于进行正确虚函数调用的Thunk):

How may I assist you today?

-5

我看到的大多数编译器实现都是将基础对象“嵌入”到派生对象中。由于在引用被评估时相对偏移量将在编译时添加,所以vtable存储的位置变得无关紧要。

多重和虚拟继承更加复杂,可能需要根据正在访问的内容使用不同的偏移量。

我强烈推荐阅读Code Project上的这篇文章:不可思议的快速C++委托。它精彩地展示了不同编译器处理继承各个方面的广泛图景。如果您对不同编译器的低级工作原理感兴趣,那么这是一篇绝佳的文章。

编辑:我链接了错误的文章。已更正。


3
“表格存放的位置变得不相关”似乎非常相关。问题的一半是在特定地问它在不同继承类型中存放在哪里。对于一个继承类型来说它无关紧要,而对于其他继承类型则变得复杂了,这并不是一个答案。此外,您还错过了问题的另一半,即如何构建虚函数表。 - underscore_d
此外,我无法看出那篇文章如何解释编译器处理继承。它所讨论的只是委托,这是一个正交的概念。 - underscore_d

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接