WEBVTT

00:00.120 --> 00:04.860
Hello again! In this video, we are going to look at pointers, references and inheritance.

00:06.240 --> 00:12.990
Usually when we have a pointer, it has a type, and it must be the address of some variable or object,

00:12.990 --> 00:14.010
which has the same type.

00:15.090 --> 00:20.610
However, if we have a pointer to the basic class in a hierarchy, then that can be the address of

00:20.610 --> 00:23.970
an object, which is any class in that hierarchy.

00:26.160 --> 00:33.960
So as an example, we have a Shape class, and a Circle which is derived from Shape. So we can have a pointer

00:34.020 --> 00:37.270
to Shape, and that could be the address of a Circle object.

00:38.220 --> 00:40.770
But you are not allowed to go in the opposite direction.

00:41.130 --> 00:44.790
You cannot have a pointer to Circle, which is the address of a Shape object.

00:46.500 --> 00:48.540
And the reason why this works is the way

00:48.540 --> 00:50.960
that the derived class is laid out in

00:51.370 --> 00:55.110
memory. It contains an object of the base class within it.

00:55.500 --> 00:58.770
So the first part of the object in memory is actually the base class.

00:59.280 --> 01:05.100
So it does make some sense to have a pointer to Shape, which is pointing to this Shape part of the Circle

01:05.100 --> 01:05.580
object.

01:06.630 --> 01:09.180
But having a Circle pointing to a Shape makes no sense!

01:11.880 --> 01:17.460
So let's try this out. So we have a Circle object and a pointer to Shape, which is the address of

01:17.460 --> 01:18.630
the Circle object.

01:22.070 --> 01:26.210
And that compiles. If we try to go the other way,

01:27.770 --> 01:31.040
so pointer to Circle as the address of a Shape object.

01:32.450 --> 01:34.940
"Cannot convert from Shape to Circle".

01:37.740 --> 01:42.900
And by the way, it works exactly the same for references. So we can have a reference to a Shape which

01:42.900 --> 01:50.580
is bound to a Circle object, but we cannot have a reference to a Circle which is bound to a Shape object.

01:51.050 --> 01:52.260
We get the same error again.

01:54.520 --> 02:01.180
Let's now add some member functions to our classes, so we have a draw() member function which -

02:01.300 --> 02:02.890
well, how do you draw a generic Shape?

02:04.120 --> 02:07.720
Then we have the draw member function, re-implemented in the Circle.

02:07.990 --> 02:13.960
So it does something which is more appropriate for a Circle. And we have another member function which

02:13.960 --> 02:15.460
will calculate the area.

02:16.680 --> 02:23.160
Then we have our pointer again, and when we call the draw() member function, the compiler will see that

02:23.160 --> 02:30.210
pShape is a pointer to the Shape class. So it is going to call this member function. And there we are.

02:30.240 --> 02:31.710
"Drawing a generic shape".

02:32.970 --> 02:35.310
And if we try to call the area member function.

02:38.290 --> 02:41.770
Then we get a compiler error. "'area' is not a member of 'Shape'".

02:44.990 --> 02:50.300
So even though this pShape is actually representing a Circle object in memory, the compiler still

02:50.300 --> 02:51.380
thinks it is a Shape.

02:58.800 --> 03:03.780
So this is a bit of a problem, because to get the full benefits of object oriented programming, we

03:03.780 --> 03:05.990
would like to be able to use pShape

03:05.990 --> 03:11.670
to represent a pointer to a Circle, or whatever subclass of Shape that it happens to be.

03:14.490 --> 03:20.130
So, for example, we could decide that we want to have a function that takes a pointer or reference

03:20.130 --> 03:24.360
to a Shape, and calls the draw() member function of the appropriate child class.

03:24.990 --> 03:26.730
Unfortunately, we have the same problem again.

03:27.030 --> 03:31.680
The compiler will see this as a reference to a Shape, and it will call the draw() member function for the

03:31.680 --> 03:36.270
Shape class. If we want to get the right member function calls,

03:36.480 --> 03:40.170
then we have to write a function which takes a Circle, by reference.

03:41.250 --> 03:46.890
And if we add another class, then we need to add another function, which takes that class by reference.

03:46.890 --> 03:47.460
And so on.

03:50.910 --> 03:57.360
So you can see if we pass a Circle, then this will call the version which takes a Circle and calls

03:57.360 --> 03:58.350
the Circle draw() member function.

03:58.860 --> 04:04.110
If we pass a reference to a Shape, then this will call the version, which takes the Shape.

04:08.240 --> 04:13.790
Another example, with the same classes. It could be that we want to have a container of pointers to

04:14.090 --> 04:19.220
child class objects, and then iterate through them and call their draw() member functions.

04:19.640 --> 04:25.010
For example, we could have some diagram or picture, and we have a vector of all the Shape objects

04:25.370 --> 04:28.850
that are needed to draw this picture. And then we can build this up.

04:29.300 --> 04:35.270
And when we are ready, we can iterate through the container and call the draw() member function of each element

04:35.270 --> 04:35.870
in the container.

04:36.110 --> 04:37.790
And that should draw the entire picture.

04:38.120 --> 04:39.770
But what happens if you try to do it?

04:41.410 --> 04:48.940
So it calls the generic member function version again, so we can add a pointer to a Circle class when

04:48.940 --> 04:53.380
it goes into the container, but when it comes out of the container, it is now a pointer to the base class.

04:53.950 --> 04:57.130
So really, we have lost all the type information when we add it to the container.

04:58.300 --> 05:01.990
One thing we could do is to extend the Shape class, and add some kind of tag.

05:02.530 --> 05:08.590
So we know what kind of object it really represents. But that makes things rather complicated.

05:09.640 --> 05:10.780
Or we could do a cast.

05:13.970 --> 05:19.610
So when the pointer comes out of the container, we cast it back to a pointer to Circle.

05:21.820 --> 05:24.340
And that does work, we do get the Circle version of draw().

05:25.150 --> 05:29.590
The problem is, if we add some different child class to the container.

05:30.010 --> 05:35.680
So for example, if we have a Triangle class and we push a Triangle on the vector. So we need somehow

05:35.680 --> 05:39.280
to know that we need to cast the first element to pointer to Circle.

05:39.700 --> 05:41.830
The second one to pointer to Triangle, and so on.

05:42.400 --> 05:44.440
So that is not really the way forward.

05:45.160 --> 05:49.690
Fortunately, C++ does have a solution for this, and we are going to find out what that is in the next

05:49.930 --> 05:50.710
lecture or two.

05:51.430 --> 05:55.120
But until then, I will see you next time and keep coding!
