I want to know what a "virtual base class" is and what it means.
Let me show an example:
class Foo
{
public:
void DoSomething() { /* ... */ }
};
class Bar : public virtual Foo
{
public:
void DoSpecific() { /* ... */ }
};
I want to know what a "virtual base class" is and what it means.
Let me show an example:
class Foo
{
public:
void DoSomething() { /* ... */ }
};
class Bar : public virtual Foo
{
public:
void DoSpecific() { /* ... */ }
};
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:
The above class hierarchy results in the "dreaded diamond" which looks like this:
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:
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.
This means that there is only one "instance" of A included in the hierarchy. Hence
Hope that helps as a mini summary. For more information, have a read of this and this. A good example is also available here.
I think you are confusing two very different things. Virtual inheritance is not the same thing as an abstract class. Virtual inheritance modifies the behaviour of function calls; sometimes it resolves function calls that otherwise would be ambiguous, sometimes it defers function call handling to a class other than that one would expect in a non-virtual inheritance.
About the memory layout
As a side note, the problem with the Dreaded Diamond is that the base class is present multiple times. So with regular inheritance, you believe you have:
But in the memory layout, you have:
This explain why when call
D::foo()
, you have an ambiguity problem. But the real problem comes when you want to use a member variable ofA
. For example, let's say we have:When you'll try to access
m_iValue
fromD
, the compiler will protest, because in the hierarchy, it'll see twom_iValue
, not one. And if you modify one, say,B::m_iValue
(that is theA::m_iValue
parent ofB
),C::m_iValue
won't be modified (that is theA::m_iValue
parent ofC
).This is where virtual inheritance comes handy, as with it, you'll get back to a true diamond layout, with not only one
foo()
method only, but also one and only onem_iValue
.What could go wrong?
Imagine:
A
has some basic feature.B
adds to it some kind of cool array of data (for example)C
adds to it some cool feature like an observer pattern (for example, onm_iValue
).D
inherits fromB
andC
, and thus fromA
.With normal inheritance, modifying
m_iValue
fromD
is ambiguous and this must be resolved. Even if it is, there are twom_iValues
insideD
, so you'd better remember that and update the two at the same time.With virtual inheritance, modifying
m_iValue
fromD
is ok... But... Let's say that you haveD
. Through itsC
interface, you attached an observer. And through itsB
interface, you update the cool array, which has the side effect of directly changingm_iValue
...As the change of
m_iValue
is done directly (without using a virtual accessor method), the observer "listening" throughC
won't be called, because the code implementing the listening is inC
, andB
doesn't know about it...Conclusion
If you're having a diamond in your hierarchy, it means that you have 95% to have done something wrong with said hierarchy.
I'd like to add to OJ's kind clarifications.
Virtual inheritance doesn't come without a price. Like with all things virtual, you get a performance hit. There is a way around this performance hit that is possibly less elegant.
Instead of breaking the diamond by deriving virtually, you can add another layer to the diamond, to get something like this:
None of the classes inherit virtually, all inherit publicly. Classes D21 and D22 will then hide virtual function f() which is ambiguous for DD, perhaps by declaring the function private. They'd each define a wrapper function, f1() and f2() respectively, each calling class-local (private) f(), thus resolving conflicts. Class DD calls f1() if it wants D11::f() and f2() if it wants D12::f(). If you define the wrappers inline you'll probably get about zero overhead.
Of course, if you can change D11 and D12 then you can do the same trick inside these classes, but often that is not the case.