Hello again! In this video, we are going to look at pointers, references and inheritance.
Usually when we have a pointer, it has a type, and it must be the address of some variable or object,
which has the same type.
However, if we have a pointer to the basic class in a hierarchy, then that can be the address of
an object, which is any class in that hierarchy.
So as an example, we have a Shape class, and a Circle which is derived from Shape. So we can have a pointer
to Shape, and that could be the address of a Circle object.
But you are not allowed to go in the opposite direction.
You cannot have a pointer to Circle, which is the address of a Shape object.
And the reason why this works is the way
that the derived class is laid out in
memory. It contains an object of the base class within it.
So the first part of the object in memory is actually the base class.
So it does make some sense to have a pointer to Shape, which is pointing to this Shape part of the Circle
object.
But having a Circle pointing to a Shape makes no sense!
So let's try this out. So we have a Circle object and a pointer to Shape, which is the address of
the Circle object.
And that compiles. If we try to go the other way,
so pointer to Circle as the address of a Shape object.
"Cannot convert from Shape to Circle".
And by the way, it works exactly the same for references. So we can have a reference to a Shape which
is bound to a Circle object, but we cannot have a reference to a Circle which is bound to a Shape object.
We get the same error again.
Let's now add some member functions to our classes, so we have a draw() member function which -
well, how do you draw a generic Shape?
Then we have the draw member function, re-implemented in the Circle.
So it does something which is more appropriate for a Circle. And we have another member function which
will calculate the area.
Then we have our pointer again, and when we call the draw() member function, the compiler will see that
pShape is a pointer to the Shape class. So it is going to call this member function. And there we are.
"Drawing a generic shape".
And if we try to call the area member function.
Then we get a compiler error. "'area' is not a member of 'Shape'".
So even though this pShape is actually representing a Circle object in memory, the compiler still
thinks it is a Shape.
So this is a bit of a problem, because to get the full benefits of object oriented programming, we
would like to be able to use pShape
to represent a pointer to a Circle, or whatever subclass of Shape that it happens to be.
So, for example, we could decide that we want to have a function that takes a pointer or reference
to a Shape, and calls the draw() member function of the appropriate child class.
Unfortunately, we have the same problem again.
The compiler will see this as a reference to a Shape, and it will call the draw() member function for the
Shape class. If we want to get the right member function calls,
then we have to write a function which takes a Circle, by reference.
And if we add another class, then we need to add another function, which takes that class by reference.
And so on.
So you can see if we pass a Circle, then this will call the version which takes a Circle and calls
the Circle draw() member function.
If we pass a reference to a Shape, then this will call the version, which takes the Shape.
Another example, with the same classes. It could be that we want to have a container of pointers to
child class objects, and then iterate through them and call their draw() member functions.
For example, we could have some diagram or picture, and we have a vector of all the Shape objects
that are needed to draw this picture. And then we can build this up.
And when we are ready, we can iterate through the container and call the draw() member function of each element
in the container.
And that should draw the entire picture.
But what happens if you try to do it?
So it calls the generic member function version again, so we can add a pointer to a Circle class when
it goes into the container, but when it comes out of the container, it is now a pointer to the base class.
So really, we have lost all the type information when we add it to the container.
One thing we could do is to extend the Shape class, and add some kind of tag.
So we know what kind of object it really represents. But that makes things rather complicated.
Or we could do a cast.
So when the pointer comes out of the container, we cast it back to a pointer to Circle.
And that does work, we do get the Circle version of draw().
The problem is, if we add some different child class to the container.
So for example, if we have a Triangle class and we push a Triangle on the vector. So we need somehow
to know that we need to cast the first element to pointer to Circle.
The second one to pointer to Triangle, and so on.
So that is not really the way forward.
Fortunately, C++ does have a solution for this, and we are going to find out what that is in the next
lecture or two.
But until then, I will see you next time and keep coding!