Pergunta

I know the usage of virtual inheritance:

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

What I want to know is the difference between

class X { public: void Bar() {} };
class Y : public virtual X {};

and

class X { public: void Bar() {} };
class Y : public X {};

mainly from the implementation perspective. I mean when would I not want to use virtual inheritance?

Foi útil?

Solução

Firstly, virtual inheritance will "merge" all identically-typed virtual bases into a single base subobject, shared by all derived types in the complete object. Do you want that to happen? If you want such bases to be represented by separate base subobjects, then you can't use virtual inheritance. So, there is an obvious matter of your intent.

In your first example, the same single A subobject will be shared by classes B, C and D within a complete object of type D. If inheritance was non-virtual, then B would have its own separate A and C would have its own separate A. This would obviously affect the behavior of any code that relies on As object identity.

So, from this point of view it is really a question to you: what do you want to implement? A single shared base subobject? Or multiple independent base subobjects?

(Your X and Y example is non-representative. The above properties of virtual inheritance only reveal itself in multiple-inheritance configurations. In single-inheritance situation virtual inheritance achieves nothing, besides imposing potential performance/space overhead.)

Secondly, in order to provide access from all derived classes to a single shared base class subobject (in case of virtual inheritance) the compiler will use run-time implementation structures. Typically derived classes will access their virtual bases through pointers embedded into each object with virtual base. This imposes a performance penalty and requires additional space in object's layout.

Meanwhile, "normal" (non-virtual) inheritance produces a fixed compile-time layout, which means that all conversions between classes are performed by adding or subtracting a fixed offset, immediately known to the compiler. This is faster and requires no additional run-time information.

Outras dicas

The difference is that the Vbtable for the virtually inheriting class will be generated. You can read about this in this article:

A vbtable (virtual base class table) is generated for multiple virtual inheritance. Because it is sometimes necessary to upclass (casting to base classes), the exact location of the base class needs to be determined. A vbtable contains a displacement of each base class' vftable which is effectively the beginning of the base class within the derived class.

Other answers are good, so read them first.

Now, let me add that with virtual inheritance:

class X { public: void Bar() {} };
class Y : public virtual X {};

you will not be able to downcast from a X reference to the enclosing Y:

Y y;
X &rx = y; // refers to the X base class subobject
Y &ry = ??? (rx); // how to get back a reference to y from rx

It is not possible to get a reference to Y from rx

  • either with static_cast<Y&> (x) as it only works for non virtual inheritance
  • or with dynamic_cast<Y&> (x) as it only works if X has some virtual function

But all you have to do is add some virtual function:

class X {
public: 
    void Bar() {} 
    virtual ~X () {}
};

It is a good idea to have a virtual destructor for a class designed to be derived from. There are few exceptions to this general rule (mostly classes with a protected destructor).

The usage of virtual inheritance is to handle multiple inheritance, like shown below

enter image description here

In your second case, the subclass D calling a (virtual) method or function from A does not know if it calls it the way through B or through C. By inherite virtually, the two "paths are fetched together".

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top