Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple Inheritance : size of class for virtual pointers?

Given the code:

class A{};

class B : public virtual A{};

class C : public virtual A{};

class D : public B,public C{};

int main(){
cout<<"sizeof(D)"<<sizeof(D);
return 0;
}

Output: sizeof(D) 8

Every class contains its own virtual pointer only not of any of its base class, So, why the Size of class(D) is 8?

like image 364
Luv Avatar asked Mar 16 '12 13:03

Luv


People also ask

Can multiple inheritance have virtual classes?

Virtual base classes are used in virtual inheritance in a way of preventing multiple “instances” of a given class appearing in an inheritance hierarchy when using multiple inheritances.

How many classes can a class inherit in multilevel inheritance?

It will make three levels of classes and two levels of inheritance. In some books, it is called multilevel inheritance. This type of inheritance is illustrated withFigure 10.4. Access specifier “protected” is very much useful in this type of inheritance.

Why virtual classes are important in the case of multiple inheritance?

Instead, if classes B and C inherit virtually from class A, then objects of class D will contain only one set of the member variables from class A. As you probably guessed, this technique is useful when you have to deal with multiple inheritance and it's a way to solve the infamous diamond inheritance.

Does VB net support multiple inheritance?

Unlike languages that allow multiple inheritance, Visual Basic allows only single inheritance in classes; that is, derived classes can have only one base class. Although multiple inheritance is not allowed in classes, classes can implement multiple interfaces, which can effectively accomplish the same ends.


3 Answers

It depends on compiler implementation. My compiler is Visual Stdio C++ 2005.

Code like this:

int main(){
    cout<<"sizeof(B):"<<sizeof(B) << endl;
    cout<<"sizeof(C):"<<sizeof(C) << endl;
    cout<<"sizeof(D):"<<sizeof(D) << endl;
    return 0;
} 

It will output

sizeof(B):4
sizeof(C):4
sizeof(D):8

class B has only one virtual pointer. So sizeof(B)=4. And class C is also.

But D multiple inheritance the class B and class C. The compile don't merge the two virtual table.So class D has two virtual pointer point to each virtual table.

If D only inheritance one class and not virtual inheritance. It will merge they virtual table.

like image 179
shihongzhi Avatar answered Oct 23 '22 04:10

shihongzhi


It depends on compiler implementation so you should specify which compiler you're using. Anyway D derives from two classes so it contains pointers to B and C vtables base class pointers (I don't know a good name for this).

To test this you may declare a pointer to B and a pointer to C and cast the address of D to base class pointer. Dump that values and you'll see they're different!

EDIT
Test made with Visual C++ 10.0, 32 bit.

class Base
{
};

class Derived1 : public virtual Base
{
};

class Derived2 : public virtual Base
{
};

class Derived3 : public virtual Base
{
};

class ReallyDerived1 : public Derived1, public Derived2, public Derived3
{
};

class ReallyDerived2 : public Derived1, public Derived2
{
};

class ReallyDerived3 : public Derived2
{
};

void _tmain(int argc, _TCHAR* argv[])
{
 std::cout << "Base: " << sizeof(Base) << std::endl;
 std::cout << "Derived1: " <<  sizeof(Derived1) << std::endl;
 std::cout << "ReallyDerived1: " <<  sizeof(ReallyDerived1) << std::endl;
 std::cout << "ReallyDerived2: " <<  sizeof(ReallyDerived2) << std::endl;
 std::cout << "ReallyDerived3: " <<  sizeof(ReallyDerived3) << std::endl;
}

Output, guess, is not surprising:

  • Base: 1 byte (OK, this is a surprise, at least for me).
  • Derived1: 4 bytes
  • ReallyDerived1: 12 bytes (4 bytes per base class because of multiple inheritance)
  • ReallyDerived2: 8 bytes (as guessed)
  • ReallyDerived3: 4 bytes (just one base class with virtual inheritance in the path but this is non virtual).

Adding a virtual method to the base you get 4 bytes more for each class. So probably extra bytes aren't vtable pointers but base class pointers used in multiple inheritance, this behavior does not change removing virtual inheritance (but if not virtual the size doesn't change adding more bases).

like image 31
Adriano Repetti Avatar answered Oct 23 '22 04:10

Adriano Repetti


First: without virtual functions, it's probable that there isn't a vptr at all in the classes. The 8 bytes you're seeing are an artifact of the way virtual inheritance is implemented.

It's often possible for several classes in a hierarchy to share the same vptr. For this to occur, it is necessary for their offset in the final class to be the same, and for the list of vtable entries in the base class to be an initial sequence the list of vtable entries in the derived class.

Both conditions are met in almost all implementations for single inheritance. No matter how deep the inheritance, there will usually be only one vptr, shared between all of the classes.

In the case of multiple inheritance, there will always be at least one class for which these requirements aren't met, since the two base classes can't have a common start address, and unless they have exactly the same virtual functions, only one's vtable could possibly be an initial sequence of the other.

Virtual inheritance adds another quirk, since the position of the virtual base relative to the class inheriting from it will vary depending on the rest of the hierarchy. Most implementations I've seen use a separate pointer for this, although it should be possible to put this information in the vtable as well.

If we take your hierarchy, adding virtual functions so that we are certain of having a vptr, we notice that B and D can still share a vtable, but both A and C need separate vtables. This means that if your classes had virtual functions, you would need at least three vptr. (From this I conclude that your implementation is using separate pointers to the virtual base. With B and D sharing the same pointer, and C with its own pointer. And of course, A doesn't have a virtual base, and doesn't need a pointer to itself.)

If you're trying to analyse exactly what is going on, I'd suggest adding a new virtual function in each class, and adding a pointer sized integral type that you initial with a different known value for each class. (Use constructors to set the value.) Then create an instance of the class, take it's address, then output the address for each base class. And then dump the class: the known fixed values will help in identifying where the different elements lie. Something like:

struct VB
{
    int vb;
    VB() : vb( 1 ) {}
    virtual ~VB() {}
    virtual void fvb() {}
};

struct Left : virtual VB
{
    int left;
    Left() : left( 2 ) {}
    virtual ~Left() {}
    virtual void fvb() {}
    virtual void fleft() {}
};

struct Right : virtual VB
{
    int right;
    Right() : right( 3 ) {}
    virtual ~Right() {}
    virtual void fvb() {}
    virtual void fright() {}
};

struct Derived : Left, Right
{
    int derived;
    Derived() : derived( 5 ) {}
    virtual ~Derived() {}
    virtual void fvb() {}
    virtual void fleft() {}
    virtual void fright() {}
    virtual void fderived() {}
};

You might want to add a Derived2, which derives from Derived and see what happens to the relative addresses between e.g. Left and VB depending on whether the object has type Derived or Derived2.

like image 43
James Kanze Avatar answered Oct 23 '22 04:10

James Kanze