In C++, What Is a Virtual Base Class

In C++, what is a virtual base class?

Virtual base classes, used in virtual inheritance, is a way of preventing multiple "instances" of a given class appearing in an inheritance hierarchy when using multiple inheritance.

Consider the following scenario:

class A { public: void Foo() {} };
class B : public A {};
class C : public A {};
class D : public B, public C {};

The above class hierarchy results in the "dreaded diamond" which looks like this:

  A
/ \
B C
\ /
D

An instance of D will be made up of B, which includes A, and C which also includes A. So you have two "instances" (for want of a better expression) of A.

When you have this scenario, you have the possibility of ambiguity. What happens when you do this:

D d;
d.Foo(); // is this B's Foo() or C's Foo() ??

Virtual inheritance is there to solve this problem. When you specify virtual when inheriting your classes, you're telling the compiler that you only want a single instance.

class A { public: void Foo() {} };
class B : public virtual A {};
class C : public virtual A {};
class D : public B, public C {};

This means that there is only one "instance" of A included in the hierarchy. Hence

D d;
d.Foo(); // no longer ambiguous

This is a mini summary. For more information, have a read of this and this. A good example is also available here.

Why must virtual base classes be constructed by the most derived class?

Because it avoids this:

class A {
public:
A(int) {}
};

class B0: virtual public A {
public:
B0(): A(0) {}
};

class B1: virtual public A {
public:
B1(): A(1) {}
};

class C: public B0, public B1 {
public:
C() {} // How is A constructed? A(0) from B0 or A(1) from B1?
};

virtual inheritance and base class of base class

Why does this work?

According to the standard (10.1.4 in the FIDS), "for each distinct baseclass that is specified virtual, the most derived object shall contain a single base class subobject of that type".

Virtual base is shared between all classes that derive from it for the instance of the object. Since a constructor may only be called once for a given instaniation of an object, you have to explicitly call the constructor in the most derived class because the compiler doesn't know how many classes share the virtual base. This is because the compiler will start from the most base class's constructor and work to the most derived class. Classes that inherit from a virtual base class directly, will not, by the standard, call their virtual base classes constructor, so it must be called explicitly.

Why do the constructor of the derived classes want to initialize the virtual base class in C++?

The constructor of virtual base is constructed. It is constructed conditionally. That is, the constructor of the most derived class calls the constructor of the virtual base. If - this is the condition - the derived class with virtual base is not the concrete class of the constructed object, then it will not construct the virtual base because it has already been constructed by the concrete class. But otherwise it will construct the virtual base.

So, you must correctly initialise the virtual base class in constructors of all derived classes. You simply must know that specific initialisation doesn't necessarily happen in case the concrete class is not the one which you are writing. The compiler doesn't and cannot know whether you will ever create direct instances of those intermediate classes, so it cannot simply ignore their broken constructors.

If you made those intermediate classes abstract, then the compiler would know that they are never the most concrete type and thus their constructor would not be required to initialise the virtual base.

wrong constructor called for virtual base class of virtual base class

The Answer is given in the comments by Sam Varshavchik.

The concept of passing the argument from each constructor to it's direct parent was the wrong way of handling the issue.

I was under the wrong impression, that I can only access the direct parents of each class. Sam Varshavchik's comment helped me to see the real problem and look for it in the right places. In one of the many classes, there was one non-virtual inheritance that made it impossible to access the constructor of the base class.

(Why) Is virtual base class constructor call required in pure virtual derived class?

Design issues (which clearly exist here) aside, I fully agree with your reasoning that, because Left is an abstract class, no Left constructor will ever have to call any Base constructor and thus it is odd that it is required.

In fact, I tested your code with several compiler versions and it does compile just fine with gcc 7.1 and onwards, with clang 3.4.1 and onwards, and with all available msvc versions.

So, my assumption is, that this was just a bug in earlier compiler versions. Can anyone confirm this?

Also note, if you change virtual void leftsMethod() = 0; to virtual void leftsMethod() {}, so that Left is not abstract anymore, the error returns, even with the latest versions. And this makes perfectly sense, as now you could instantiate a Left instance and thus it would be up to Left's constructor to call one of Base's constructors.

Possible workaround

If you cannot switch to a newer compiler and if you cannot alter the implementation of Base, this may be a working solution for you:

Define a dummy instance of CustomType somewhere. Then, you can provide the "required" default constructors like this:

class CustomType{};

class Base
{
public:
Base( CustomType& obj ) : refObj_( obj ) {}
private:
CustomType& refObj_;
};

static CustomType dummy;

class Left : public virtual Base
{
protected:
Left() : Base(dummy) {}; // note here
public:
virtual void leftsMethod() = 0;
};

class Right : public virtual Base
{
protected:
Right() : Base(dummy) {}; // and here
public:
virtual void rightsMethod() = 0;
};

class Bottom : public Left, public Right
{
public:
Bottom( CustomType& obj ) : Base( obj ), Left(), Right() {}
virtual void leftsMethod() override {}
virtual void rightsMethod() override {}
};

void test()
{
CustomType c;
Bottom b(c);
}

This is just to make the compiler happy, your argument that these constructors will never call Base(dummy) still holds.

Note however, that Base should really have a virtual destructor! I don't know if you just didn't include it here for brevity or if it really doesn't have one. If it doesn't, it's a very bad idea to build a class hierarchy on top of it.

Virtual base class data members

As a practice you should only use virtual inheritance to define interfaces as they are usually used with multiple inheritance to ensure that only one version of the class is present in the derived class. And pure interfaces are the safest form of multiple inheritance. Of course if you know what you are doing you can use multiple inheritance as you like, but it can result in brittle code if you are not careful.

The biggest drawback with virtual inheritance is if their constructors take parameters. If you have to pass parameters to the constructor of a virtual base class you force all derived classes to explicitly call the constructor (they cannot rely on a base class calling the constructor).

The only reason I can see for your explicit advise is that data in your virtual base class these might require constructor parameters.

Edit
I did some home work after Martin's comment, thank Marin. The first line is not quite true:

As a practice you should only use
virtual inheritance to define
interfaces as they are usually used
with multiple inheritance to ensure
that only one version of the class is
present in the derived class.

Virtual inheritance makes no difference if the base class is a pure interface (except for slightly different compiler errors, in vc8, if all the methods are not implemented). It only makes a real difference if the base class has data, in this case you end up with a diamond rather than a U shape

Non virtual    virtual
A A A
| | / \
B C B C
\ / \ /
D D

In the virtual case B and C share the same copy of A.

However I still agree with everything else about pure interfaces being the safest form of multiple inheritance, even if they don't require virtual inheritance. And the fact that constructor parameters and virtual inheritance are a pain.



Related Topics



Leave a reply



Submit