Search code examples
inheritancepolymorphismvirtualmultiple-inheritancevtable

Is the location of the virtual pointer in an object different if the object has polymorphism compared to multiple inheritance?


I am trying to understand virtual pointer placement within an object and would like some clarification please. I have looked at two different scenarios which are polymorphism and multiple inheritance. Both have different answers for where the virtual pointer is.


Polymorphism
The virtual pointer is at the top of the object and only for that class, meaning there is only one virtual pointer. For example:

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

class B : A {
    public:
        int num;

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

The object in memory would then look like:

| vPointer to class B vTable |
| int num                    |



Multiple Inheritance
There is multiple virtual pointers, one for each class. The vTables for this classes however are changed so that overwritten methods and directed to the address of the current classes function code. However I am thinking that this would mean that each class would potentially have multiple different vTables. For example:

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();
}

The object in memory would then look like:

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

Are both or either of these correct? I have searched around but can only find clarification on multiple inheritance with nothing on polymorphism.

Any help would be much appreciated.

Thank you.


Solution

  • The standard says nothing about implementation of virtual functions and polymorhphism (vtables are not even mentionned). Nothing either about memory layout of derived classes, except that object isa memory regio and contains base subobjects.

    My answer is hence not a general statement about C++ standard, but only a pragmatic explanation about how implementations generally behave.

    If you are interested in such implementation aspects , I highly recommend you this DDJ article which explains also more complex cases, such as virtual inheritance.

    Polymorphism with single inheritance

    Polymorphism is indeed implemented by using an object pointer to a virtual table (shared by all objects of the class), containing pointer to the virtual functions. This article explains very well how it works.

    The vtable pointer is stored at the beginning of the object, because its the most efficient way to ensure that the virtual function can be called efficiently without knowing the exact layout of the object pointed to:

    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
    

    So it's exacly as you describe. You can always get confirmation by looking at the assembler code generated by your compiler, to see how the implicit constructor loads the vtable into the object.

    Polymorphism with multiple inheritance

    Multiple inheritance, means that your derived object C has two subobjects, one for A and one for B. Each of the subobject must manage its vtable as any other object of its type. This means that there are two vtables, each at the begin of the subobject:

    | 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                    | 
    

    This is required, because you can have code like this:

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

    But the derived class C is a class of its own, and there is a virtual function defined as well:

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

    So this means there is also a vtable for the D. Where is it stored ? It must be at the begin of the object. So the vtable for C will be a superset of the vtable of A:

    | 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                    | 
    

    Here the assembler generated by MSVC2013 for the vtables:

    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