0:04
In this episode, we will present
the notion of multiple inheritance in C++.
Before we dive into this matter, we will start by reviewing
all the knowledge aquired up to now.
Now, you are familiar with the three central concepts of the object oriented programming:
encapsulation/abstraction, inheritance and polymorphism.
You know that encapsulating and abstracting means to regrouping
data and processing into one same entity.
It also revolves around separating the user interface from the implementation details.
Also, you know that the inheritance lets us set up a "is a" relationship
between different classes of a program. You also know that this tool
lets us use polymorphic solutions
in the sense of "inclusion polymorphism".
Finally, you have learnt that the polymorphisms is a very powerful tool
making it possible for the execution of a same code
to automatically adapt to the different types of data.
In C++, you know that we are able to set up a polymorphic
solution using two mandatory ingredients:
we must use virtual methods through references or pointers.
To conclude this brief review of your knowledge,
let us mention the notions of "abstract classes*
and "pure virtual methods". These let us improve
the object-ortiented models dramatically.
Let us move on to the thema of this episode: "multiple inheritance*.
It actually follows an already mastered thema, the simple inheritance.
Until now, the inheritance relationships we have studied
were simple inheritance relationships.
This means that each sub-class only had one direct parent.
Multiple inheritance simply revolves
around one sub-class having several direct parent classes.
Multiple inheritance is allowed in C++. In other words, we may
have one sub class inherit directly from several parent classes.
However, this notion is not provided by all
object-oritented programming languages.
Basic notions regarding multiple inheritance are mostly
the same as the ones regarding simple inheritance.
Consequently, one sub-class will inherit from these base classes, all their attributes
and methods (except constructors and destructor), as well as their type.
2:07
Let us dive into a concrete example; we shall see how
multiple inheritance will deem useful.
For old time's sake, let us choose a game-related example.
Now, several entities will play a part in the game. For example,
a ball, rackets, a net, a player.
On each entity, we will bestow a method called "evolue" (TN: means "evolve")
making it possible to track the position of the entity during the game.
In our first draft conception, we could imagine
a super-class "Entite" (TN: means "Entity") whose main method
will be called "evolue". This method will probably be polymorphic and
redefined in the sub-classes. However, this is not our point here. Then, we will have
sub-classes representing the different concrete entities in the game
such as the classes "Balle", "Raquette", "Filet" and "Joueur" (TN: means "Ball", "Racket", "Net", and "Player")
Now, after further analysis, we might realise that, due to game requirements,
all these entities do not, in fact, behave exactly
the same. They might not have the same needs.
For example, we may need to print or draw certain entities.
The ball, the net or the racket may require a graphical representation aswell.
Others may not require it. For example, we may not need to
see the player in the game.
Moreover, certain entities may need to be interactive,
that is, controllable with mouse and keyboard (maybe the ball and the racket).
Others may not need to be controllable : for example, maybe we need not
control the player nor the net.
We are now wondering how to set up all this.
The class hierarchy (which you can now see) answers precisely
to the just-now-mentioned needs. Here, we have introduced
a super-class "Interactive", thanks to which we will take care
of the different entities requiring to be interactive,
such as the ball and the racket.
This super-class will provide the methods regarding
this very interaction.
Similarly, the super-class "Graphique" lets us define
all entities we wish to see drawable in the game
(that is, the racket, ball and the net).
this super-class will provide the necessary material
to the graphical representation of these entities.
These two super-classes let us see some entities
- albeit not all - as interactive objects
and/or as graphical objects.
Please note that it would not have been correct to place the drawing and
4:23
interaction methods in the super-class "Entity".
Why, do you think, is it the case?
Indeed, placing these methods in the super-class "Entity" would force certain
classes to have a drawing method while we want them to be undrawable.
It would also force certain classes to be interactive
while we do not wish it.
Thanks to multiple inheritance, we now have a better model
of what we wish to realise. C++ grants us this option.
We can imagine many another situation requiring
the use of multiple inheritance.
Here comes a zoological example. Assume that you are tasked to model
the animals in a zoo. As it happens, some animals
are ovoviviparous and this inherit from the oviparous
and from the viviparous.
Multiple inheritance lets us model clearly
the different lodgers of the zoo.
Oviparous animals lay eggs and the egg "develops" the embryo
until birth. Viviparous animals give birth to live babies.
Ovoviviparous animals (such as the seahorse) indeed inherit
the characteristics of oviparous and viviparous both:
their embryo is "developed" by the egg (just like oviaprous),
but they are born live (just like viviparous).
Finally, a more informatic example. You may not know that the different
C++ classes regarding inputs and outputs,
(these ones should ring a bell),
are organized thanks to multiple inheritance.
This organisation in particular, presents the famous,
"diamond architecture" on which we will expand
later on.
6:02
Let us now move on to the concrete use of multiple inheritance in C++.
In C++, if we wish to declare that a sub-class inherits from several
parent classes, we simply need, during the class declaration,
to indicate all the super-classes, separated by commas.
Thus, here, for each super-class from which the sub-class inherits
we will indicate "public nameOfTheSuperClass",
and separate the different parent classes with commas.
Concretely, this here means that the class "Ovovivipare" inherits
from the super-class "Ovipare", and the super-class "Vivipare".
Then, the class's content is not very different from what we have seen
in the classes written or read until now.
Finally, please note that, the order of the declaration
of the inheritance links is relevant. Indeed, it impacts
how we will build and destroy an instance
created through multiplte inheritance.
This is the next point we will focus on.
How do construction and destruction work in the frame
of multiple inheritance.
Let us discuss what mutiple inheritance implies on constructors.
There is in fact not much new; although there is one point
you must pay attention to.
So, like with simple inheritance, the initialization of the attributes inherited
from the super-classes is done through the initialization list
of the sub-class constructor by calling all the constructors
of the parent classes we inherit from.
Thus, the general syntax is the same as for simple inheritance.
In the "initialization list" section of the sub-class constructor,
we call, like this, the different constructors of the super-classes
we inherit from, separated by commas. And, obviously,
if one of the super-classes has a default constructor,
it is not necessary to explicitly call it. As always,
while you are still unfamiliar with the constructor concepts,
we recommand you explicitly call the default constructors aswell
so that you do not forget that there is a call to the default constructor,
even though this is not necessary.
However, be careful: there is a little peculiarity with multiple inheritance.
The call order of the constructors is not the order
of the written constructors in the initialization list of the sub-class
constructors. The constructors are called following the order
of the inheritance declaration.
As always, the destructors are called
in the order reverse to that of the constructors.
Let us illustrate all this with a concrete example.
We will go back to the example regarding ovoviviparous who inherit
from both oviparous and viviparous. Let us assume that
oviparous' constructor typically require
a number of eggs, and that the viviparous' constructor
requires a gestation period. For ovoviviparous, we will add
an attribute here: it is a boolean indicating
if the species is rare or not.
Thus, we pass here as the third parameter an argument "rareté" (TN: means "rarity").
This lets us initialize wether the species is rare or not.
Therefore, the prototype of the ovoviviparous' constructor will typically
have one parameter for the oviparous' constructor,
one parameter for the viviparous' constructor, and one parameter
to initialize its own attribute. To this parameter, we give a default
value, which is "false".
The constructor for ovoviviparous is thus tasked with calling the constructors
of the classes it inherits from. First of all, it will call,
in its initialization list, the constructors of the classes
it inhertis from. For example, it will called the viviparous' constructor
by passing the expected parameter. It will also call the oviparous' constructor
by passing the corresponding parameter. These calls are, as usual, separated
by commas. Then, we will initialize its own attribute with the parameter
received here as argument.
Pay attention though, here, even if
we may think that, as it is, the constructor of ovoviviparous will
first call the viviparous' constructor, then the oviparous' constructor,
actually, this is not the case.
The first called constructor will actually be the one of oviparous.
What matters regarding the call order of the super-classes
in case of multiple inhertiance is indeed the inheritance declaration
and not the order of declaration in the constuctor's
initialization list in the sub-class.
This means that here, whatever we write, the oviparous' constructor
will always be called first. Then, the viviparous' constructor
will be called.
To make things easier, most modern compilers will now indicate
with a warning if the call order
of the constructors does not match
the one you think you have
written here.
Voilà, this was all we had to say on multiple inheritance
and the order of the constructors' call.
We remind you that the destructors are always called
in the reverse order of the constructors'.