如果对象具有多态性与多重继承相比,对象中虚拟指针的位置是否不同?

时间:2014-11-15 04:49:28

标签: inheritance polymorphism virtual multiple-inheritance vtable

我正在尝试理解对象中的虚拟指针放置,并希望得到一些澄清。我已经研究了两种不同的场景,即多态和多继承。对于虚拟指针的位置,两者都有不同的答案。


多态性
虚拟指针位于对象的顶部,仅适用于该类,这意味着只有一个虚拟指针。例如:

class A {
    public:
        virtual void walk();
}

class B : A {
    public:
        int num;

        virtual void walk();
        virtual void run();
}

内存中的对象如下所示:

| vPointer to class B vTable |
| int num                    |



多重继承
有多个虚拟指针,每个类一个。然而,这些类的vTable被更改,以便覆盖方法并定向到当前类的地址函数代码。但是我认为这意味着每个类可能会有多个不同的vTable。 例如:

class A {
    public:
        virtual void walk();
}

class B {
    public: 
        char name;            

        virtual void run();
}

class C : A, B {
    public:
        int num;

        virtual void run();
        virtual void walk();
        virtual void swim();
}

内存中的对象如下所示:

| vPointer to class A vTable |
| vPointer to class B vTable |
| char name                  |
| int num                    |

这些都是正确的吗?我已经四处搜索,但只能找到关于多重继承的澄清,而没有关于多态的任何内容。

非常感谢任何帮助。

谢谢。

1 个答案:

答案 0 :(得分:1)

该标准没有提及虚函数和多态性的实现(甚至没有提到vtable)。没有关于派生类的内存布局,除了该对象是内存区域并包含基础子对象。

因此,我的答案不是关于C ++标准的一般性陈述,而只是关于实现通常如何表现的实用解释。

如果您对此类实施方面感兴趣,我强烈建议您 this DDJ article ,这也解释了更复杂的情况,例如虚拟继承。

具有单一遗传的多态性

多态性确实是通过使用指向虚拟表的对象指针(由类的所有对象共享)实现的,其中包含指向虚函数的指针。 article 非常好地解释了它的工作原理。

vtable指针存储在对象的开头,因为它是确保在不知道指向的对象的确切布局的情况下有效调用虚函数的最有效方法:

A a; B b;    // example assumes that class B inherits A publicly 
A *p = &b;   // pointer to a base object for showing polymorphism in action   
...
p->walk();   // at this stage, you don't know if p points to A or B.  
             // Bus as vtable pointer ist at the begin of the object, you don't have to care

所以你所描述的很棒。您可以通过查看编译器生成的汇编代码来获得确认,以查看隐式构造函数如何将vtable加载到对象中。

具有多重继承的多态性

多重继承,意味着派生对象C有两个子对象,一个用于A,一个用于B.每个子对象必须将其vtable作为其类型的任何其他对象进行管理。这意味着有两个vtable,每个都在子对象的开头:

| vPointer to class A vTable |
| data for A object          |   => here it's empty
| vPointer to class B vTable |
| char name                  |   => here the data for the b object 
| int num                    | 

这是必需的,因为您可以使用以下代码:

C c; 
A *pa = &c; B *pb = &c;
pa->walk(); pb->run(); 

但派生类C是它自己的类,并且还定义了一个虚函数:

C *pc = &c;
pc->walk(); pc->run(); pc->swim(); 

所以这意味着还有一个V的vtable。它存储在哪里?它必须位于对象的开头。因此,C的vtable将成为A的vtable的超集:

| vPointer to class C vTable |   => the first functions in the table are those of A, followed by all the virtual functions of C.  
| data for A object          |   => here it's empty
| vPointer to class B vTable |
| char name                  |   => here the data for the b object 
| int num                    | 

这里是由MSVC2013为vtable生成的汇编程序:

CONST   SEGMENT
??_7C@@6BB@@@ DD FLAT:??_R4C@@6BB@@@            ; C::`vftable' loaded at begin of B object
    DD  FLAT:?run@B@@UAEXXZ                       ; this is in fact the vtable for B
CONST   ENDS
CONST   SEGMENT
??_7C@@6BA@@@ DD FLAT:??_R4C@@6BA@@@            ; C::`vftable' loaded at begin of C object
    DD  FLAT:?walk@A@@UAEXXZ                      ;  that's the subset for A 
    DD  FLAT:?swim@C@@UAEXXZ                      ;  that's the superset for C specific gunctions
CONST   ENDS