c++inheritancemultiple-inheritancevirtual-inheritanceobject-layout

Virtual inheritance in C++


I found this in a website while reading about virtual inheritance in c++

When multiple inheritance is used, it is sometimes necessary to use virtual inheritance. A good example for this is the standard iostream class hierarchy:

//Note: this is a simplified description of iostream classes

class  ostream: virtual public ios { /*..*/ }
class  istream: virtual public ios { /*..*/ }

class iostream : public istream, public ostream { /*..*/ } 
//a single ios inherited

How does C++ ensure that only a single instance of a virtual member exists, regardless of the number of classes derived from it? C++ uses an additional level of indirection to access a virtual class, usually by means of a pointer. In other words, each object in the iostream hierarchy has a pointer to the shared instance of the ios object. The additional level of indirection has a slight performance overhead, but it's a small price to pay.

i am confused with the statement:

C++ uses an additional level of indirection to access a virtual class, usually by means of a pointer

could anybody explain this?


Solution

  • Basically, if virtual inheritance is not used, base members are actually part of derived class instances. Memory for the base members is allocated in each instance, and no further indirection is necessary to access them:

    class Base {
    public:
        int base_member;
    };
    
    class Derived: public Base {
    public:
        int derived_member;
    };
    
    
    Derived *d = new Derived();
    int foo = d->derived_member;  // Only one indirection necessary.
    int bar = d->base_member;     // Same here.
    delete d;
    

    However, when virtual inheritance comes into play, virtual base members are shared by all classes in their inheritance tree, instead of several copies being created when the base class is multiply inherited. In your example, iostream only contains one shared copy of the ios members, even though it inherits them twice from both istream and ostream.

    class Base {
    public:
        // Shared by Derived from Intermediate1 and Intermediate2.
        int base_member;  
    };
    
    class Intermediate1 : virtual public Base {
    };
    
    class Intermediate2 : virtual public Base {
    };
    
    class Derived: public Intermediate1, public Intermediate2 {
    public:
        int derived_member;
    };
    

    That means an extra indirection step is required in order to access virtual base members:

    Derived *d = new Derived();
    int foo = d->derived_member;  // Only one indirection necessary.
    int bar = d->base_member;     // Roughly equivalent to
                                  // d->shared_Base->base_member.
    delete d;