WEBVTT

00:00:00.840 --> 00:00:05.060
The smart winter class from the previous lesson has one major flaw.

00:00:05.440 --> 00:00:09.530
What if we declare a new pointer, c2, and initialize

00:00:09.530 --> 00:00:12.620
it with the c pointer. As you know,

00:00:12.620 --> 00:00:15.640
this will implicitly call the copy constructor, which

00:00:15.640 --> 00:00:18.350
by default creates a shallow copy.

00:00:18.360 --> 00:00:19.240
In other words,

00:00:19.240 --> 00:00:23.260
both of these pointers will now point to the same resource in memory.

00:00:23.540 --> 00:00:26.830
This is the reason why we redefined copy semantics in the

00:00:26.830 --> 00:00:30.510
Subject class, because shallow copy will just make a copy

00:00:30.510 --> 00:00:32.360
of the memory address itself.

00:00:32.740 --> 00:00:36.150
But we want our smart pointers to have an exclusive

00:00:36.150 --> 00:00:39.970
ownership of the resource, so copy semantics should actually

00:00:39.970 --> 00:00:42.850
be disabled in the smart_ptr class.

00:00:43.840 --> 00:00:46.940
We can achieve this by assigning the copy constructor and

00:00:46.940 --> 00:00:49.660
the copy assignment operator to delete.

00:00:50.040 --> 00:00:53.000
If we don't do that, then we are risking the potential case

00:00:53.000 --> 00:00:56.600
when two smart pointers point to the same place in memory and

00:00:56.600 --> 00:00:58.860
both of them try to deallocate it.

00:00:59.440 --> 00:01:03.820
Unlike the example with the DNA when we made a deep copy of the resource,

00:01:03.830 --> 00:01:07.970
in this case, we don't want to do anything with the resource itself.

00:01:07.980 --> 00:01:12.850
Smart pointers are here just to point to them and manage their lifetime.

00:01:13.140 --> 00:01:17.120
Since we want exclusive ownership, only the last pointer that

00:01:17.120 --> 00:01:20.160
has the address should manage that resource.

00:01:20.540 --> 00:01:24.800
So in this case I would want the c2 pointer to take the ownership from

00:01:24.800 --> 00:01:29.660
the pointer c. This kind of sounds like we need to steal the resource,

00:01:29.670 --> 00:01:35.070
and that is exactly what we need to do. Stealing resources and

00:01:35.080 --> 00:01:40.330
invalidating the previous owner is the job of the move semantics. Move

00:01:40.330 --> 00:01:45.140
constructor will take the address from the other raw pointer and set it

00:01:45.140 --> 00:01:50.850
to null. Move assignment operator will do the same, but before that we

00:01:50.850 --> 00:01:55.870
also need to deallocate the current resource, if there is one. Otherwise,

00:01:55.870 --> 00:01:57.360
we would risk a memory leak.

00:01:59.740 --> 00:02:04.020
So if we want to transfer the ownership from one pointer to another,

00:02:04.020 --> 00:02:06.250
we need to do it explicitly.

00:02:06.640 --> 00:02:09.630
Let's include the utility header, because we will need

00:02:09.630 --> 00:02:11.860
to use the move function to do this.

00:02:12.940 --> 00:02:17.830
Okay, and now if I want to transfer the ownership from the pointer c, I first

00:02:17.830 --> 00:02:21.360
need to use the move function to turn it into an x value.

00:02:21.740 --> 00:02:25.430
This will force the c2 pointer to use the move constructor and

00:02:25.430 --> 00:02:29.060
steal the resources. Let's compile this now.

00:02:29.940 --> 00:02:31.540
No segmentation faults,

00:02:31.550 --> 00:02:34.910
no errors, which means that the resource was successfully

00:02:34.910 --> 00:02:39.520
transferred and deallocated. What we implemented here is a

00:02:39.520 --> 00:02:43.140
simpler version of a smart pointer from the standard library,

00:02:43.140 --> 00:02:46.160
also known as the unique pointer.

00:02:47.040 --> 00:02:49.760
We need to include the memory header to use it.

00:02:50.440 --> 00:02:54.060
The name unique stems from the fact that this kind of

00:02:54.060 --> 00:02:56.460
pointer has an exclusive ownership.

00:02:57.440 --> 00:03:01.540
Only one unique pointer can point to the same resource in memory,

00:03:01.550 --> 00:03:04.460
which in that context makes it unique.

00:03:04.840 --> 00:03:09.360
Let's replace our own implementation with this unique_ptr class.

00:03:09.360 --> 00:03:13.600
Implicit conversion will not work here because unique pointers have

00:03:13.600 --> 00:03:18.320
an explicit constructor, so we can only use this new expression as a

00:03:18.320 --> 00:03:20.850
constructor parameter, like this.

00:03:21.740 --> 00:03:25.310
Let's compile this now to make sure that it works and that the complex

00:03:25.310 --> 00:03:29.660
object is correctly deallocated. It seems like it is.

00:03:30.340 --> 00:03:30.980
However,

00:03:30.990 --> 00:03:34.170
it is not a good practice to allocate resources with the new

00:03:34.170 --> 00:03:37.870
operator when using unique pointers. Instead,

00:03:37.870 --> 00:03:42.260
you should initialize this pointer by using the special make_unique function.

00:03:43.140 --> 00:03:47.660
Just like new operator, make_unique will allocate an object on the heap.

00:03:48.240 --> 00:03:51.830
The type of the object should be passed in as a template argument,

00:03:51.840 --> 00:03:55.630
and we can pass the constructor parameters for that object through

00:03:55.630 --> 00:03:57.750
the make_unique function arguments.

00:03:58.740 --> 00:04:00.390
Unlike the new operator,

00:04:00.390 --> 00:04:03.960
this function will actually return a unique_ptr which points

00:04:03.960 --> 00:04:05.960
to the allocated resource on the heap.

00:04:07.140 --> 00:04:08.540
Since that's the case,

00:04:08.540 --> 00:04:13.050
we can simply use automatic deduction here to declare a new unique pointer.

00:04:13.440 --> 00:04:16.140
Using make_unique function is optional.

00:04:16.149 --> 00:04:20.850
It was initially introduced to resolve a specific exception safety issue,

00:04:20.850 --> 00:04:26.390
but that issue was fixed since C++ 17, so as of now,

00:04:26.390 --> 00:04:29.450
the only reason to use this function instead of the new

00:04:29.450 --> 00:04:33.280
operator is to be clear that this newly allocated resource

00:04:33.280 --> 00:04:35.950
will be owned by some unique pointer.

00:04:36.340 --> 00:04:40.200
Some people also prefer it because they can use the auto keyword here,

00:04:40.200 --> 00:04:43.290
but I think that the most important reason to use make_unique

00:04:43.300 --> 00:04:47.180
is to stay consistent. In the next lesson, we will talk about

00:04:47.180 --> 00:04:50.540
another type of a smart pointer which is instantiated by a

00:04:50.540 --> 00:04:52.180
similar function to this one.

00:04:52.190 --> 00:04:55.860
So we can use this make_unique function to be explicit about the

00:04:55.860 --> 00:04:58.860
ownership type of the allocated resources.

00:05:00.440 --> 00:05:02.630
Now that we know how unique pointers work,

00:05:02.640 --> 00:05:07.760
let's use them to improve our two custom classes DNA and Subject.

00:05:08.240 --> 00:05:12.250
We know that Subject should have an exclusive ownership over the DNA

00:05:12.250 --> 00:05:16.360
objects, so this is a perfect use case for unique pointers.

00:05:16.740 --> 00:05:19.050
Don't forget to include the memory header.

00:05:20.040 --> 00:05:23.370
So every subject has a sample pointer which points

00:05:23.370 --> 00:05:25.460
to the DNA object on the heap.

00:05:26.340 --> 00:05:32.070
Let's turn this raw sample pointer into a unique_ptr. Since we prefer

00:05:32.070 --> 00:05:36.190
using the make_unique function over the new operator, let's modify the

00:05:36.190 --> 00:05:41.130
member initializer list of the three constructors. Default constructor

00:05:41.130 --> 00:05:46.250
makes a DNA with zeros, and Parameterized constructor makes one with a

00:05:46.250 --> 00:05:47.260
string literal.

00:05:48.740 --> 00:05:52.240
The Copy constructor should also use make‑unique to create a new

00:05:52.240 --> 00:05:55.550
object by using the DNA's copy constructor.

00:05:56.340 --> 00:05:58.810
So for now everything is still the same.

00:05:58.810 --> 00:06:03.800
We just replaced new with make_unique. But here is where the power of

00:06:03.800 --> 00:06:08.660
unique pointers kicks in. Move constructor will steal the resources

00:06:08.660 --> 00:06:13.640
from the other subjects, so all we have to do is to apply std::move

00:06:13.650 --> 00:06:15.760
on this other sample pointer.

00:06:16.140 --> 00:06:19.970
This will transfer ownership of the DNA resource from one unique

00:06:19.970 --> 00:06:23.640
pointer to another, so this simple pointer from the other

00:06:23.640 --> 00:06:26.460
temporary subject will be invalidated.

00:06:26.840 --> 00:06:30.260
That means that we don't have to set this other sample pointer

00:06:30.260 --> 00:06:33.160
to null, unique_ptr will take care of this.

00:06:34.140 --> 00:06:36.860
Same goes for the Move assignment operator.

00:06:38.240 --> 00:06:41.880
We just moved the DNA from one pointer to another, so we

00:06:41.880 --> 00:06:44.150
don't have to set this one to null either.

00:06:45.040 --> 00:06:49.500
But we also don't have to delete the old DNA object, because once the

00:06:49.500 --> 00:06:52.960
unique_ptr gets the ownership of this other resource,

00:06:52.970 --> 00:06:57.960
the old resource will automatically get deallocated to prevent memory leaks.

00:06:59.340 --> 00:07:02.300
The same is true for the Subject's destructor.

00:07:02.310 --> 00:07:06.530
We can remove it altogether, because the memory management of the DNA

00:07:06.530 --> 00:07:10.950
resource is now the responsibility of the sample unique_ptr.

00:07:11.340 --> 00:07:13.200
When Subject goes out of scope,

00:07:13.210 --> 00:07:17.980
the sample member will also be removed, and if the sample is removed,

00:07:17.990 --> 00:07:21.360
then the resource it points to is also deallocated.

00:07:21.740 --> 00:07:25.340
So you can see that by using unique pointers, memory

00:07:25.340 --> 00:07:28.550
management is a lot easier and more intuitive.
