We have seen that, in C++;
a class can inherit from several super-classes.
We call this multiple inheritance.
What happens if several super-classes
provide, to one of their sub-classes a member with the same name?
This ambiguous situation
will be the subject
of today's video.
The rules on multiple inheritance's access rights
are the same as simple inheritance's.
This means that a sub-class can directly access the members
(i.e., attributes and methods)
protected (or public, of course) from the super-classes.
The new question,
regarding multiple inheritance is:
"What happens if several super-classes
provide to a sub-class
attributes and methods with the same name?"
Let us use a concrete example.
We will go back to
our zoological hierarchy.
Here, we had two super-classes "Ovipare" (Oviparous) and "Vivipare" (Viviparous).
From those inherited the sub-class "Ovovivipare" (Ovoviviparous).
Let us now suppose that each of the super-classes
included a method "afficher". (TN: means "print")
This method permits, for example,
to print the instances' characterisitcs for each of these classes.
The class Ovovivipare inherits
from the classes Ovipare and Vivipare.
Therefore, it will inherit
two methods "afficher".
The method "afficher" from the class Ovipare
and the method "afficher" from the class Vivipare.
These two methods' name is strictly identical.
Let us now suppose that,
elsewhere in the program (for example in a main program),
we create an instance of Ovovivipare.
On this instance, we call the method "afficher".
Now, we are wondering which method will be called?
The one inherited from Ovipare or the one inherited from Vivipare?
The compiler would ask itself
the very same question
and thus refuse this instruction.
For the compiler, there is an ambiguity,
There is a problem of shadowing
meaning that an identifier is shadowing another
and the compiler knows not
in which scope to solve this call.
Be careful,
in this case, we are indeed facing a shadowing problem:
a scope resolution problem.
Indeed, the call of the method "afficher"
on an instance of Ovovivipare
will trigger a compiling error.
This will happen even if the method "afficher" does not have the same parameters
in the classes "Ovipare" and "Vivipare".
A priori, this can seem unexpected.
Let us suppose, for example,
that, in the class "Ovipare" the method "afficher" does not have parameters
while, in the class "Vivipare",
the method "afficher" has one string-type parameter.
We could imagine that calling the method "afficher"
by providing a string-type parameter
should lift all ambiguity,
for it appears obvious that this method should be called.
Yet, the ambiguity could be lifted here
only in situations where overloading plays a part.
But, as you have already seen
in the previous episodes,
in C++, overloading can only occur within the same scope.
This means that here,
this has nothing to do with an overloading problem.
Therefore, the compiler will still refuse this instruction.
Indeed, the method "afficher" without parameters
is in the scope of the class "Ovipare",
while the method "afficher" with one string-type parameter
is in the scope of the class "Vivipare".
These two methods are not in the same scope.
Consequently, we cannot consider
that this method "afficher" here is an overloading
of this other method "afficher" here.
We cannot use an overloading-tied mechanism
to lift the ambiguity.
We shall thus resolve the scope appropriately.
Now, how should we proceed
in order to solve this scope resolution problem?
First option :
We could simply use the scope resolution operator.
Here, for example
in the main program creating the instance of Ovipare,
upon calling the method "afficher",
we could precise in which scope we are seeking this method "afficher".
For example, we could say that,
on the instance "o", we call the method "afficher" of the class Vivipare.
Thus, we are establishing the link between the method and the class
through the scope resolution operator.
However, this way to solve the problem,
that is, through the external use of the scope resolution operator,
is not the correct solution.
Whyso?
Because it delegates to the user of the class Ovovivipare
the choice on how to print an ovoviviparous.
This task clearly lies on the shoulders of the programmer
of the class Ovovivipare.
The idea would be, for a better solution,
to have the sub-class
(in our case, the sub-class Ovovivipare),
indicating which method inherited from the sub-class it wishes to see called.
To this end, in C++,
we can add to the sub-class a special declaration indicating
which ambiguous methods or attributed inherited from higher
should be called through the instances of the sub-class.
This declaration's syntax is the following :
we use the reserved keyword "using",
followed by the name of the ambiguous attribute or method.
Then, we indicate
through the scope resolution operator
in which super-class
we will seek this attribute or method.
In the case of our example,
we could, in the Ovovivipare class,
indicate with such a declaration that the method from Vivipare
will be chosen to print an Ovovivipare.
In other words, when the method "afficher" will be applied on
an instance of Ovovivipare,
we will search for this method in the class "Vivipare".
Pay close attention to the syntax :
during the declaration,
we will use neither parentheses nor return type
for this method.
We simply signify that we wish to use
the method called "afficher" from the class Vivipare.
This is solution is better than the former,
for now the very class "Ovovivipare"
decides how to print its own instances.
This choice does not lie upon the class users anymore.
To make things even clearer,
it is possible to have the class "Ovovivipare"
contain a method dedicated to indicate
how its instances will be printed.
This way, things are as explicit as humanly possible :
it is not necessary to go through the class
to make sure there is "using" declaration
indicating how to print an Ovovivipare.
This way, we know that an Ovovivipare has its own printing method.
This method will decide
how these printings will occur exactly.
We could perfectly decide that, for example,
to print an Ovovivipare,
we print both the characteristics of Ovipare
and of Vivipare.
In this case, the method "afficher"
of Ovovivipare will call the two methods,
using the scope resolution operator
to lift the amiguity.
Here, the implementation choice
designed for the method "afficher" of Ovovivipare
is to call both methods "afficher"
(the two inherited namesake methods) :
first the method "afficher" inherited from the class "Ovipare",
then the method "afficher" inhrited from the class "Vivipare".
This here is a good solution
where the sub-class provides an explicit method
in order to clarifiy the ambiguous calls.
This concludes the sequence
on shadowing in the case of multiple inheritance.