A generalized basic rule from item 7.
Suppose we have an inheritance hierarchy defined like this:
|
|
According to item 33, this will lead to following behavior:
|
|
The reason for this two-faced behavior if that non-virtual functions like B::mf
and D::mf
are statically bound (item 37), which means that non-virtual functions invoked through pB will always be those defined for class B, even if pB points to an object of a class derived from B
. This mismatched behavior exhibits inconsistent behavior, since any given D object may act like either a B or a D (depending on the pointer type) when mf
is called.
On the other hand, virtual functions are dynamically bound (item 37), so if mf
were a virtual function, a call to mf
through either pB or pD would result in an invocation of D::mf
, because pB and pD really point to is an object of type D.
Worse than the inconsistent behavior problem in the real world, the example above basically shows a contradiction in class design, theoretically:
- public inheritance means is-a (item 32), so everything that applies to B object also applies to D object
- a non-virtual function in the base class establishes an invariant over specialization that classes derived from
B
must inherit both the interface and the implementation (item 34), andmf
is such an non-virtual function inB
As long as we redefine the mf
, an inherited non-virtual function, one of the conditions above will break, leading to two solutions: either make the mf
virtual, or change the class relationship from ‘is-a’ to something else.
In fact, this is exactly the same argument in item 7, which exlains why destructors in polymophic base classes should be virtual. In essence, item 7 is nothing more than a special case of this item.