Hello again!
In this video, we are going to look at multiple inheritance.
Multiple inheritance means that a derived class has more than one parent.
It is rather a controversial subject!
Many programmers and object-oriented
designers have strong views about whether multiple inheritance is desirable, or even if it should be
allowed at all!
There are some languages which do not support inheritance. Java, for example. C++ does, which is
why I am making this video.
The main argument against multiple inheritance is that it adds complexity. It is even worse than
single inheritance for creating very complicated hierarchies, full of duplicated code.
However, if you use it intelligently, it can be a good thing.
One situation where multiple inheritance is useful is for writing
what are known as "mix-in" classes. If you have two classes which represent entirely separate concepts.
For example, one class which interacts with files and another class which interacts with users.
You could "mix" these into a single class which allows users to interact with files.
As an example, we could have a TouchResponder class, which is part of the user interface.
So this will it responds to mouse clicks, key presses, taps on a screen, and so on.
Then we have a HardwareDevice class which communicates with the actual physical electronics, hardware.
And then we have things like a mouse, a keyboard, a touch screen and so on, which combine both these
properties.
So we could write a Mouse class which inherits from the TouchResponder and also from the HardwareDevice.
So this can respond to mouse button clicks, and also communicate with the physical mouse.
To declare this Mouse class. It is similar to single inheritance, except we have more than one parent.
So we put a comma between them.
So the mouse class inherits from the HardwareDevice and the TouchResponder.
Here is a very skeletal example.
We have the HardwareDevice class, the TouchResponder class. And then we have the Mouse, Keyboard
and TouchScreen which all inherit from these.
And then we can create objects of that class.
So that is allowed. In terms of memory layout,
We have all the base classes at the start of the object, followed by the derived class.
So for example, with the Mouse. We have the HardwareDevice at the start of memory, because that is
declared first, then the TouchResponder, and then the actual derived object, the Mouse. The constructors
will be called in the same order.
So HardwareDevice constructor, then TouchResponder, then Mouse. For the destructors, that goes in the reverse
order.
So the Mouse destructor first, then TouchResponder,
Then HardwareDevice.
The derived class will inherit all the members of the parents, provided they were declared as public
or protected.
If we have the HardwareDevice with a get_data() member function, and the TouchResponder with
a touch_down() function.
Then we can write a member function for the Mouse class which will call the touch_down().
So if the user clicks on a button. And then it can call the get_data() member function, to get the actual
data from the physical device.
So here is a rather more fleshed out skeleton.
We have the HardwareDevice class, which has a constructor and destructor.
It has the initialize() member function, which I will come back to in a minute. And then the get_data()
member function.
In the TouchResponder class.
We have the initialize() function which has a different signature from the HardwareDevice one.
Then we have the touch_down() function.
And then in our Mouse class, we call the member functions it has inherited.
So the mouse will inherit the get_data() and the initialize() from the HardwareDevice, and it will
inherit touch_down() and initialize() from the Responder.
And then, in the main() function, we can create a Mouse object, and call its process() member function.
So there we are.
The constructors are called in the order they were declared.
Then we call the touch_down() and get_data() from the parent classes. And then the destructors are called, in
reverse order.
Now about this initialize() member function. What happens if we call this?
You may think that this acts like an overload.
We have one version which has a paramater reference
argument and one version which has no argument.
So the compiler should resolve this.
But in fact, if we try to compile this, we get an error.
"Ambiguous access of initialize".
So even though the two functions have different signatures, the compiler cannot understand which one
we want.
So it is probably a bit hard to follow that in all the codes.
Let's break it out.
So we have the HardwareDevice initialize(), taking "params" by reference.
We have the TouchResponder initialize(), with no arguments.
And then we called initialize() for the mouse, with no arguments.
The Mouse class will inherit both these member functions, so these are available to call.
But if they -
But if you have member functions which have the same name, they must only be defined in one parent.
So that is why that did not compile.
We can avoid this by writing the function initialize() for the Mouse class. And this can do whatever we
need to do.
If we are calling the parents, we need to give the class name, so the compiler will be clear which one we
are calling. So we can call the initialize() memberfunction for the TouchResponder and then the HardwareDevice,
member function.
And then with the same code as before.
But now we have the initialize() member function for the Mouse. And then we can call this from the main() function.
Okay, so that is it for this video.
I will see you next time.
But until then, keep coding!