WEBVTT

00:00.120 --> 00:00.690
Hello again!

00:01.050 --> 00:04.410
In this video, we are going to look at unique_ptrs and polymorphism.

00:06.020 --> 00:12.440
Polymorphism is when we have a class hierarchy, and we can use an object of the base class to represent

00:12.440 --> 00:15.140
any developed class object in that hierarchy.

00:16.460 --> 00:21.530
In C++, we have to have a pointer to the base or a reference to the base, to be able to do that.

00:23.200 --> 00:24.640
We could have some code like this.

00:24.970 --> 00:31.660
We have some Shape objects which will make up a diagram or a picture, and we want to use a vector to

00:31.660 --> 00:34.270
build up the elements of this picture.

00:34.840 --> 00:36.190
And then, once we have got it,

00:36.430 --> 00:39.070
we can iterate through the elements and draw the picture.

00:40.870 --> 00:47.200
So we have a vector of Shapes, and then we add Circles, Triangles and Squares to this vector.

00:47.710 --> 00:52.120
And then we iterate through the vector, and we call the draw member of each [object].

00:53.020 --> 00:58.540
For this to work with polymorphism, we need to use a pointer to the base class. And this more-or-less

00:58.540 --> 01:02.230
forces us to allocate the child objects, using new.

01:02.710 --> 01:05.590
So in here, we are going to get a pointer to a Circle object.

01:06.230 --> 01:09.040
We are going to store this in the vector, as a pointer to a Shape object.

01:09.820 --> 01:15.740
And then, when we iterate, we call the virtual draw() member function, and that will call the draw()

01:15.840 --> 01:21.490
member function of the Circle. And the same for the Triangle and the Square. Because we are using new, we have

01:21.490 --> 01:23.020
to manage the memory ourselves.

01:23.470 --> 01:26.050
So we have to remember to release the memory,

01:26.500 --> 01:27.970
once we have finished with this vector.

01:31.880 --> 01:32.990
So let's try this out.

01:32.990 --> 01:35.240
We have our Circle, Triangle and Square.

01:35.600 --> 01:38.150
We have a virtual draw() member function function.

01:41.020 --> 01:47.560
We have our vector of pointers to Shapes, and then we allocate Circle, Triangle and Square objects, and

01:47.560 --> 01:48.820
we push them onto the vector.

01:49.300 --> 01:55.030
Then we iterate and we call the virtual member function. And then we need to remember to delete

01:55.030 --> 01:58.270
all the pointers in this vector, once we have finished using the vector.

02:00.010 --> 02:00.610
So there we are.

02:02.570 --> 02:06.890
Now that we know about unique_ptrs, we should use them wherever we can.

02:07.310 --> 02:12.710
If you see a call to new and a traditional pointer, then you should think about whether that could

02:12.710 --> 02:15.230
be replaced by using a unique_ptr.

02:15.800 --> 02:16.820
And in this case, it can.

02:17.300 --> 02:18.440
It is a little bit involved.

02:18.440 --> 02:20.690
But on, the other hand, we do not have to manage the memory anymore.

02:21.410 --> 02:23.060
So we call make_unique().

02:23.600 --> 02:26.420
We use the derived type as the parameter.

02:26.780 --> 02:32.300
So this will allocates the memory for the child class. And it will return a unique_ptr to the child

02:32.300 --> 02:32.720
class.

02:33.440 --> 02:37.370
And then we move that into a unique_ptr to the base class.

02:38.270 --> 02:44.630
So this "pBase" is a unique_ptr to a base class object, which is actually pointing to a derived class

02:44.630 --> 02:45.140
object.

02:48.590 --> 02:54.020
So this gives us all the advantages of having a unique_ptr, and it avoids all the drawbacks of using a

02:54.020 --> 02:55.010
traditional pointer.

02:58.410 --> 03:01.020
So the codes would look like this, with the same classes

03:01.020 --> 03:06.270
again. We have our vector, which is now a vector of unique_ptrs to Shape.

03:07.200 --> 03:11.910
Then instead of calling new, we call make_unique(), to create the child class objects.

03:12.780 --> 03:18.300
And then we iterate and call the draw() member function. And we do not need to remember to

03:18.300 --> 03:18.600
call delete.

03:19.050 --> 03:23.430
When the function returns, the destructor for the vector will be called. This

03:23.430 --> 03:28.800
will call the destructors for the elements. With the pointer destructor, that does nothing, but with the

03:28.800 --> 03:31.740
unique_ptr destructor, that will release the memory.

03:32.400 --> 03:35.460
So we do not need to worry about memory management in this code.

03:37.660 --> 03:39.730
And yes, it does give the right answer!

03:41.350 --> 03:43.630
You may have heard about "design patterns".

03:44.020 --> 03:49.210
There was a very famous book, published in the 1990s, which set out several design patterns.

03:49.540 --> 03:52.300
So these are approaches for doing object oriented programming.

03:53.050 --> 03:57.820
And one of the best known is the factory pattern. In the factory pattern,

03:57.850 --> 04:00.940
you have a function which creates new objects.

04:01.360 --> 04:05.230
And the type of the object will depend on the arguments to the function.

04:06.010 --> 04:08.740
So, in C++, you can use this with a class hierarchy.

04:09.310 --> 04:13.630
The arguments to the function will say what kind of object to create, which child object.

04:14.200 --> 04:16.660
And then you return it through a pointer to the base class.

04:18.850 --> 04:24.490
So this means that all the code for creating objects in the hierarchy is in a single place, in the factory

04:24.490 --> 04:24.940
function.

04:25.600 --> 04:26.530
It is very flexible.

04:26.950 --> 04:31.930
You just pass an argument, which will decide which child object gets created, and you get a polymorphic

04:31.930 --> 04:32.560
object back.

04:33.220 --> 04:36.880
And if you add new child classes, it is very easy to extend the code.

04:37.150 --> 04:39.730
You just go to this function and make the necessary changes.

04:43.600 --> 04:49.600
The traditional way to write a factory pattern in C++ is to use new to create the object, then [return]

04:49.600 --> 04:52.240
a pointer, which has to be deleted by the caller.

04:55.380 --> 04:56.960
So it looks like this.

04:57.000 --> 04:58.950
So here is our factory function.

04:59.430 --> 05:01.110
create_shape().

05:01.770 --> 05:03.900
This will return a pointer to the base class.

05:04.980 --> 05:08.160
The argument here, is the number of sides that the object has.

05:08.670 --> 05:10.530
So if we have one side, that is a circle.

05:10.830 --> 05:12.420
If it has three sides, it is a triangle.

05:12.750 --> 05:14.600
And if it has four sides, it is a square.

05:16.180 --> 05:19.540
So we call new to create the object, and then we return

05:19.540 --> 05:21.640
that, through a pointer to the base class.

05:24.970 --> 05:29.950
And then, in our main() function we call create_shape(), we pass the number [as the] argument.

05:30.220 --> 05:33.310
So 3 is going to create a triangle object.

05:33.850 --> 05:38.200
So "pShape" will be a pointer to the base class, which is pointing to a Triangle object.

05:39.460 --> 05:44.260
And then we can call its draw() function, and then we need to release the memory.

05:49.080 --> 05:49.760
And there we are.

05:49.770 --> 05:50.760
We have drawn a triangle.

05:54.320 --> 05:57.410
So again, can we implement this using a unique_ptr?

05:57.980 --> 05:58.610
Yes, we can.

05:58.940 --> 06:03.140
So we create a unique_ptr to the child class, as a local variable.

06:04.040 --> 06:04.820
Then we return

06:04.820 --> 06:06.410
that unique_ptr from the function.

06:06.860 --> 06:09.860
This is going to be moved into a unique_ptr object in the caller.

06:10.370 --> 06:15.560
So the allocated memory has been transferred, from this local variable, into the caller's unique_ptr.

06:16.130 --> 06:21.650
And then, when that object goes out of scope in the caller, the destructor will be called and the memory

06:21.650 --> 06:23.090
will automatically be released.

06:27.100 --> 06:33.760
So now, our factory function returns a unique_ptr to the base class. We call make_unique() to create

06:33.760 --> 06:34.990
the child class object.

06:37.680 --> 06:43.040
And when we call the function, we move the return value into our own unique_ptr.

06:44.940 --> 06:47.580
And then we can just call the virtual member function on the pointer.

06:48.120 --> 06:52.320
And when the object goes out of scope, the memory will be released automatically.

06:55.460 --> 06:56.090
So there we are.

06:56.690 --> 06:58.070
Okay, so that is it for this video.

06:58.370 --> 06:59.150
I will see you next time.

06:59.420 --> 07:01.340
Until then, keep coding!
