Virtual inheritance is an important thing when we are talking about multiply inheritance.
Basically you can find term 'virtual base class' which means that the base class that is met in inheritance tree is shared between derived classes.
Let's look on the inheritance tree w/o virtual base class
class A
{
};
class B : public A
{
};
class D : public A
{
};
class E : public B, public D
{
};
Class E will have 2 copies of A(derived from B and D). To be more concrete let's look at the output of -fdump-class-hierarchy g++ option
Class A
size=1 align=1
base size=0 base align=1
A (0xb7f32680) 0 empty
Class B
size=1 align=1
base size=1 base align=1
B (0xb7f326c0) 0 empty
A (0xb7f32700) 0 empty
Class D
size=1 align=1
base size=1 base align=1
D (0xb7f32740) 0 empty
A (0xb7f32780) 0 empty
Class E
size=2 align=1
base size=2 base align=1
E (0xb7f327c0) 0 empty
B (0xb7f32800) 0 empty
A (0xb7f32840) 0 empty
D (0xb7f32880) 1 empty
A (0xb7f328c0) 1 empty
Indeed, the most obvious is the overhead: E contains 2 instances of A(by addresses 0xb7f32840 and 0xb7f328c0).
The other thing you are unable to call methods of A from E directly. There is no distinct path from E to A. The following code wouldn't be compiled. The compiler will raise an error that reference to methodA is ambiguous.
class A
{
public:
virtual void methodA(){}
};
class B : public A
{
};
class D : public A
{
};
class E : public B, public D
{
virtual void methodE(){ methodA(); }
};
In this case you should explicitly call methodA either from B or D
virtual void methodE(){ B::methodA(); D::methodA(); }
Also you can face a problem with A as a base of E
A *a = new E;//‘A’ is an ambiguous base of ‘E’
You say you can do smth like this
A *a;
E *e = new E;
void *v = (void *)e;
a = (A *)v;
No chance to expect defined behavior with this piece of c-ish code.
Now let's look how things change w/ virtual base class.
class A
{
};
class B : virtual public A
{
};
class D : virtual public A
{
};
class E : public B, public D
{
};
Class A was defined as a virtual base class in the code above. Let's look what g++ says
Class A
size=1 align=1
base size=0 base align=1
A (0xb7f7a680) 0 empty
Class B
size=4 align=4
base size=4 base align=4
B (0xb7f7a6c0) 0 nearly-empty
vptridx=0u vptr=((& B::_ZTV1B) + 12u)
A (0xb7f7a700) 0 empty virtual
vbaseoffset=-0x00000000c
Class D
size=4 align=4
base size=4 base align=4
D (0xb7f7a7c0) 0 nearly-empty
vptridx=0u vptr=((& D::_ZTV1D) + 12u)
A (0xb7f7a800) 0 empty virtual
vbaseoffset=-0x00000000c
Class E
size=8 align=4
base size=8 base align=4
E (0xb7f7a880) 0
vptridx=0u vptr=((& E::_ZTV1E) + 12u)
B (0xb7f7a8c0) 0 nearly-empty
primary-for E (0xb7f7a880)
subvttidx=4u
A (0xb7f7a900) 0 empty virtual
vbaseoffset=-0x00000000c
D (0xb7f7a940) 4 nearly-empty
subvttidx=8u vptridx=12u vptr=((& E::_ZTV1E) + 24u)
A (0xb7f7a900) alternative-path
Class E has one instance of A(by address 0xb7f7a900). We got rid of overhead. In the output you can see that there is only one path from A to E. There is no problem with compiling the following code
class E : public B, public D
{
virtual void methodE(){ methodA(); }
};
And A can be used as a base class for E
A *a = new E;
With virtual inheritance we achieve better object model but we can loose some performance in order to run-time resolving paths to base from derived and from base to derived classes through v-table. With small classes we can even get overhead if v-table is pretty big.
The other thing you should be aware that c-style casting between derived and base classes(both ways) may break your program. Use dynamic_cast instead. That is because with the v-table classes not more of POD(Plain Old Data) types. c-style casts won't work correctly with non-POD types.