1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
|
As illustrated in fig(InternalOrganization), an tt(AirAuto) represents
em(two) tt(Vehicle)s. This not only results in an i(ambiguity) about which
function to use to access the tt(mass) data, but it also defines two
tt(mass) fields in an tt(AirAuto). This is slightly redundant, since we can
assume that an tt(AirAuto) has but one mass.
It is, however, possible to define an tt(AirAuto) as a class consisting of
but one tt(Vehicle) and yet using multiple derivation. This is realized by
defining the base classes that are multiply mentioned in a derived class's
inheritance tree as a
hi(base class: virtual) emi(virtual base class).
For the class tt(AirAuto) this implies a small change when deriving an
tt(AirAuto) from tt(Land) and tt(Air) classes:
verb(
class Land: virtual public Vehicle
{
// etc
};
class Auto: public Land
{
// etc
};
class Air: virtual public Vehicle
{
// etc
};
class AirAuto: public Auto, public Air
{
};
)
Virtual derivation hi(virtual derivation) ensures that a tt(Vehicle) is
only added once to a derived class. This means that the route along which a
tt(Vehicle) is added to an tt(AirAuto) is no longer depending on its direct
base classes; we can only state that an tt(AirAuto) is a tt(Vehicle). The
internal organization of an tt(AirAuto) after virtual derivation is shown in
fig(VirtualBaseClass).
figure(polymorphism/virtbase)
(Internal organization of an tt(AirAuto) object when the base
classes are virtual.)
(VirtualBaseClass)
When a class tt(Third) inherits from a base class tt(Second) which in turn
inherits from a base class tt(First) then the tt(First) class constructor
called by the tt(Second) class constructor is also used when this tt(Second)
constructor is used when constructing a tt(Third) object. Example:
verb(
class First
{
public:
First(int x);
};
class Second: public First
{
public:
Second(int x)
:
First(x)
{}
};
class Third: public Second
{
public:
Third(int x)
:
Second(x) // calls First(x)
{}
};
)
The above no longer holds true when tt(Second) uses virtual derivation.
When tt(Second) uses virtual derivation its base class constructor is
em(ignored) when tt(Second)'s constructor is called from tt(Third). Instead
tt(Second) will by default call tt(First)'s default constructor. This is
illustrated by the next example:
verbinsert(CLASSES)(polymorphism/examples/virtualinherit.cc)
When constructing tt(Third) tt(First)'s default constructor is used by
default. tt(Third)'s constructor, however, may overrule this default behavior
by explicitly specifying the constructor to use. Since the tt(First) object
must be available before tt(Second) can be constructed it must be specified
first. To call tt(First(int)) when constructing tt(Third(int)) the latter
constructor can be defined as follows:
verb(
class Third: public Second
{
public:
Third(int x)
:
First(x), // now First(int) is called.
Second(x)
{}
};
)
This behavior may seem puzzling when simple linear inheritance is used but
it makes sense when multiple inheritance is used with base classes using
virtual inheritance. Consider tt(AirAuto): when tt(Air) and tt(Auto) both
virtually inherit from tt(Vehile) will tt(Air) and (Auto) both initialize the
common tt(Vehicle) object? If so, which one will be called first? What if
tt(Air) and tt(Auto) use different tt(Vehicle) constructors? All these
questions can be avoided by passing the responsibility for the initialization
of a common base class to the class eventually using the common base class
object. In the above example tt(Third). Hence tt(Third) is provided an
opportunity to specify the constructor to use when initializing tt(First).
Multiple inheritance may also used to inherit from classes that do not all use
virtual inheritance. Assume we have two classes, tt(Derived1)
and tt(Derived2), both (possibly virtually) derived from tt(Base).
We will address the question which constructors will be called when calling a
constructor of the class tt(Final: public Derived1, public Derived2).
To distinguish the involved constructors tt(Base1) indicates the tt(Base)
class constructor called as base class initializer for tt(Derived1) (and
analogously: tt(Base2) called from tt(Derived2)). A plain tt(Base) indicates
tt(Base)'s default constructor.
tt(Derived1) and tt(Derived2) indicate the base class initializers used when
constructing a tt(Final) object.
Now we're ready to distinguish the various cases when constructing an object
of the class tt(Final: public Derived1, public Derived2):
itemization(
it() classes:
verb(
Derived1: public Base
Derived2: public Base
)
quote(This is normal, non virtual multiple derivation. The
following constructors are called in the order shown:
verb(
Base1,
Derived1,
Base2,
Derived2
)
)
it() classes:
verb(
Derived1: public Base
Derived2: virtual public Base
)
quote(Only tt(Derived2) uses virtual derivation.
tt(Derived2)'s base class constructor is ignored. Instead,
tt(Base) will be called and it will be called prior to any
other constructor:
verb(
Base,
Base1,
Derived1,
Derived2
)
As only one class uses
virtual derivation, there will still be em(two) tt(Base) class
objects in the eventual tt(Final) class.
)
it() classes:
verb(
Derived1: virtual public Base
Derived2: public Base
)
quote(Only tt(Derived1) uses virtual derivation.
tt(Derived1)'s base class constructor is ignored. Instead,
tt(Base) will be called and it will be called prior to any
other constructor. Different from the first (non-virtual) case
tt(Base) is now called, rather than tt(Base1):
verb(
Base,
Derived1,
Base2,
Derived2
)
)
it() classes:
verb(
Derived1: virtual public Base
Derived2: virtual public Base
)
quote(Both base classes use virtual derivation and so
only em(one) tt(Base) class object will be present in the
tt(Final) class object. The following constructors are called
in the order shown:
verb(
Base,
Derived1,
Derived2
)
)
)
Virtual derivation is, in contrast to virtual functions, a pure
i(compile-time) issue. Virtual inheritance merely defines
how the compiler defines a class's data organization and construction process.
|