Question

I'm having a problem with C++ virtual inheritance.

I have a class hierarchy as follows:

class Base
{
public:
    virtual void Func(){};
    int BaseValue;
};

class Derived : virtual public Base
{
public:
    void Func(){};
    virtual void Func2(){};
    int DerivedValue;
};

Which compiles soundly, however, I'm a little confused about the memory structure.

I wish to have the result ofsizeof(Derived)==20, namely:

  1. BaseValue and DerivedValue--8 bytes
  2. Pointer denotes the member offset of Base class (feature of virtual inheritance)--4 bytes
  3. Pointer denotes the virtual function table of Base class--4 bytes
  4. Pointer denotes the virtual function Func2() which only belongs to class Derived--4 bytes (as far as I'm concerned, derived class which has no non-virtual base classes and gets its unique virtual functions should has its own virtual table)

Which sum up to 20 bytes;

yet Xcode 4.6 produces a different result of sizeof(Derived)==16, do I get it wrong?

Was it helpful?

Solution

Pointer denotes the virtual function table of Base class--4 bytes
Pointer denotes the virtual function Func2() which only belongs to class Derived--4 bytes (as far as I'm concerned, derived class which has no non-virtual base classes and gets its unique virtual functions should has its own virtual table)

Ah, I see the problem now. That's not quite how virtual function tables work. When Base is defined, the compiler notices it requires a virtual table, and generates a virtual table for Base, with one pointer (Func), which points to the Base::Func implementation. When Derived is defined, the compiler notices it inherits from Base, and generates a function table for Base that has two pointers, Func points to Derived::Func, and Func2 points to Derived::Func2.

Then, if an instance of Base is created, it's function table pointer that you mention points at the Base table, and any calls to Func will be redirected to Base::Func.

If an instance of Derived is created, it's internal Base object's virtual function table pointer will point at the Derived table instead. Base only knows how to access the Func pointer, but that Func pointer points at Derived::Func now, and so that's what get's called. It doesn't realize it's pointing at a different table. In code, it might look more like this:

using voidFunctionType = void(*)();

struct BaseVTable {
    voidFunctionType Func;
}BaseVTableGlobal; 

struct Base {
    Base() :vTable(&BaseVTableGlobal) {}
    void Func() {vTable->Func();}

    BaseVTable* vTable; //4 bytes
    int BaseValue; //4 bytes
}; //total is 8 bytes

struct DerivedVTable : public BaseVTable  {
    voidFunctionType Func;
    voidFunctionType Func2;
}DerivedVTableGlobal; 

//inherits 8 bytes, +4 for virtual inheritance = 12
struct Derived : virtual public Base { 
    Derived() :Base() {vTable = &DerivedVTableGlobal;} //the shared vTable points at DerivedVTableGlobal
    void Func() {vTable->Func();} //base knows about Func, so this is easy
    void Func2() {((DerivedVTable*)vTable)->Func2();} //base doesn't know about Func2

    int DerivedValue; //4 bytes
}; //16 bytes total

So XCode is right. Derived is "hijacking" Bases virtual function table, and in fact, that's exactly how virtual functions do their magic.

(Assumptions everywhere, none of this is well-defined, virtual inheretence complicates things, etc etc etc)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top