Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Consequences of changing inheritance to virtual?

I'm working on a huge project that I didn't start. My task is to add some additional functionality to what already is there. I'm in a situation where I have to use virtual inheritance because I have a diamond model. The situation is depicted in the following illustration:

     Base class
    /           \
   /             \
My new class      A class that was there before (OldClass)
   \             /
    \           /
     \         /
      \       /
    My other new class

For this to work, both the classes in the middle have to inherit from the base through public virtual instead of just public. So:

class OldClass: public BaseClass {}

has to become:

class OldClass: public virtual BaseClass {}

Since this project is really huge and I'm working on a small part of it, I don't want to break it by doing this. My adhoc tests worked and the program seems to work fine, but I'm still skeptic.

So my question is: What side effects and consequences should I expect by adding the virtual keyword? Is there anything to worry about?

like image 867
The Quantum Physicist Avatar asked Mar 23 '17 14:03

The Quantum Physicist


2 Answers

The immediate consequence is that for regular inheritance, derived classes invoke the constructor of the immediate base, while for virtual inheritance, the most derived class (i.e. the one being instantiated directly) does, as this is the only place that would know all the virtual bases.

Compare:

struct A { A(int) { } };
struct B : A { B(int i) : A(i) { } };
struct C : B { C(int i) : B(i) { } };

vs

struct A { A(int) { } };
struct B : virtual A { B(int i) : A(i) { } };
// wrong: struct C : B { C(int i) : B(i) { } };
struct C : B { C(int i) : A(i), B(i) { } }; // correct

Also, the initializer behaviour is different, because the initializer for A in B is ignored if B is not the most derived class:

struct A { A(int i) { cout << 'A' << i; } };
struct B : virtual A { B(int i) : A(i+1) { cout << 'B' << i; } };
struct C : B { C(int i) : A(i+1), B(i+1) { cout << 'C' << i; } };

A a(0);        // prints A0
B b(0);        // prints A1B0
C c(0);        // prints A1B1C0

If you had non-virtual inheritance here (which would force you to remove the A initializer in the C constructor, the third line would output A2B1C0.

like image 111
Simon Richter Avatar answered Sep 25 '22 23:09

Simon Richter


In addition to what Simon Richter said about calling constructors, using virtual inheritance means that you should be a bit more careful with your casts: You need to use dynamic_cast<> whenever you downcast a pointer in a hierarchy that includes virtual inheritance, as the relative offset between the base object and the goal type of the cast depends on the concrete actual type of the object. Other than that, everything else should work as expected.

like image 27
cmaster - reinstate monica Avatar answered Sep 26 '22 23:09

cmaster - reinstate monica