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?
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
.
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.
This is hard to answer in this abstract way, because it all depends on what the classes are doing and how you use them.
Having virtual inheritance means that your two middle classes will share the same Base
. Is that what you want?
There is no language rule against actually having two separate Base
classes in the hierarchy. It's just a bit harder to call member functions, because you have to explicitly indicate which copy you want to call by prefixing the function name p->NewClass::base_function()
or p->OldClass::base_function();
. That works if sharing Base
data is not what you need.
And like Serge says, if some other class only inherits one Base
, it will still just contain one Base.
According to the standard, virtual inheritance is exactly the same as non virtual one except that only one single instance of the virtualy inherited class exists in the derived object.
So in nothing in original code had multiple inheritance on classes derived from Base
, changing the inheritance of Base
to be virtual should not change behaviour. But you must consult of build the class hierachy to be sure of it.
Refs from n4096 draft:
10.1 Multiple base classes [class.mi]
...
4 A base class specifier that does not contain the keyword virtual, specifies a non-virtual base class. A base class specifier that contains the keyword virtual, specifies a virtual base class. For each distinct occurrence of a non-virtual base class in the class lattice of the most derived class, the most derived object (1.8) shall contain a corresponding distinct base class subobject of that type. For each distinct base class that is specified virtual, the most derived object shall contain a single base class subobject of that type.
And except from the examples following that paragraph, I could find no other reference to virtual inheritance in [class.mi].
来源:https://stackoverflow.com/questions/42979185/consequences-of-changing-inheritance-to-virtual