WEBVTT

00:00:00.740 --> 00:00:06.120
Most programmers will frown upon the usage of raw pointers in modern C++,

00:00:06.130 --> 00:00:10.720
and they actually have a good reason to do so. I'm not saying that raw

00:00:10.720 --> 00:00:13.340
pointers don't have their own place, after all,

00:00:13.340 --> 00:00:17.130
we cannot escape from legacy on which the C++ was built on,

00:00:17.140 --> 00:00:21.430
but you should only use them if you have a specific reason, or if

00:00:21.430 --> 00:00:25.410
you are working with legacy code. Since we are now familiar with raw

00:00:25.410 --> 00:00:28.490
pointers and dynamic memory allocation,

00:00:28.500 --> 00:00:31.860
learning about smart pointers will be effortless.

00:00:32.240 --> 00:00:37.250
So, what are these smart pointers anyway, and what makes them so smart?

00:00:37.640 --> 00:00:42.870
The answer can be summed up in one word: ownership. To explain this,

00:00:42.870 --> 00:00:47.110
I created a simple smart pointer class. Just like the standard

00:00:47.110 --> 00:00:51.520
array is a wrapper around a C style array, smart pointer is a

00:00:51.520 --> 00:00:53.660
wrapper around the raw pointer.

00:00:54.740 --> 00:00:57.730
This is also a templated class because the pointer should

00:00:57.730 --> 00:00:59.960
be able to point to any type of data.

00:01:00.540 --> 00:01:03.590
Smart pointer is an RAII class,

00:01:03.600 --> 00:01:08.690
its responsibility is to manage resources at which this raw pointer is

00:01:08.690 --> 00:01:13.530
pointing to. A smart pointer has two main states,

00:01:13.540 --> 00:01:18.550
it can either be empty, which means that its raw pointer is pointing to null.

00:01:19.140 --> 00:01:22.160
This is the result of using the default constructor.

00:01:22.840 --> 00:01:25.650
Other state is when the pointer is pointing to some

00:01:25.650 --> 00:01:29.480
resource. The address of this resource is received here,

00:01:29.490 --> 00:01:31.750
through the constructor's parameter.

00:01:32.440 --> 00:01:35.290
Raw pointers also have these two states,

00:01:35.290 --> 00:01:37.860
they can either point to something or not.

00:01:38.340 --> 00:01:40.350
However, unlike the raw pointer,

00:01:40.360 --> 00:01:44.040
a smart pointer assumes the ownership of the pointed to

00:01:44.040 --> 00:01:47.750
resources. Once the smart pointer goes out of scope,

00:01:47.760 --> 00:01:50.650
it will deallocate the resources from the heap.

00:01:51.540 --> 00:01:53.520
If this still seems kind of abstract,

00:01:53.530 --> 00:01:57.060
let's go down to the main function and try it out.

00:01:57.070 --> 00:02:02.300
Notice that I'm again using the Complex class from one of the previous lessons.

00:02:02.430 --> 00:02:07.350
I also added a special message to the constructor and destructor, so that we

00:02:07.350 --> 00:02:11.160
know when the complex object is constructed or destroyed.

00:02:12.040 --> 00:02:15.370
Until now, we used raw pointers to get access to

00:02:15.370 --> 00:02:17.560
allocated resources on the heap.

00:02:17.940 --> 00:02:21.470
We allocate a complex object with the new operator and assign

00:02:21.470 --> 00:02:24.060
the returned memory address to the c pointer.

00:02:24.840 --> 00:02:29.640
Let's now replace this raw pointer with a smart one. We need to pass

00:02:29.640 --> 00:02:33.760
the objects data type as a template argument. Also,

00:02:33.760 --> 00:02:38.040
we don't need the asterisk character anymore, because this c variable is now a

00:02:38.040 --> 00:02:43.530
smart pointer object. Implicit conversion will send the returned memory address

00:02:43.530 --> 00:02:46.850
from the heap to the smart pointer's constructor.

00:02:47.540 --> 00:02:50.380
Now the raw pointer inside of a smart pointer is

00:02:50.380 --> 00:02:52.360
pointing to this complex subject.

00:02:52.740 --> 00:02:54.140
Once the main scope ends,

00:02:54.150 --> 00:02:59.080
the smart pointer will be removed, because it belongs to the stack, but the

00:02:59.080 --> 00:03:03.990
destructor of the smart pointer will call delete on the internal raw pointer,

00:03:04.000 --> 00:03:07.370
and since that pointer points to the complex object,

00:03:07.380 --> 00:03:09.750
that object will be removed too.

00:03:10.640 --> 00:03:13.450
This is similar to the subject class we were working on,

00:03:13.460 --> 00:03:16.770
because when the subject goes out of scope, the DNA code

00:03:16.770 --> 00:03:19.060
from the heap gets deallocated too.

00:03:19.940 --> 00:03:21.060
If I compile this,

00:03:21.060 --> 00:03:25.850
you can see that the complex object is constructed and destructed properly.

00:03:26.240 --> 00:03:31.230
And this is actually unusual, because complex object is allocated on the heap.

00:03:31.240 --> 00:03:34.550
If this was a raw pointer, we would have a memory leak.

00:03:35.040 --> 00:03:39.220
But since the smart pointer assumes the ownership over the allocated resource,

00:03:39.220 --> 00:03:41.350
we can use dynamic objects from the heap,

00:03:41.350 --> 00:03:45.130
just like any other stack allocated object, they will

00:03:45.130 --> 00:03:47.850
be managed by the surrounding scope.

00:03:48.240 --> 00:03:52.390
Higher level languages like Python or JavaScript don't usually allow

00:03:52.390 --> 00:03:55.160
you to explicitly use different parts of memory.

00:03:55.640 --> 00:03:58.670
Smart pointers abstract away the dangerous parts of

00:03:58.680 --> 00:04:01.200
memory management, like memory leaks,

00:04:01.200 --> 00:04:06.160
but they also provide us with the same low‑level access to memory as C.

00:04:07.340 --> 00:04:12.630
For now, this custom smart pointer class is just an RAII class which

00:04:12.630 --> 00:04:16.060
deallocates the owned resources in the destructor.

00:04:16.440 --> 00:04:17.640
But as we know,

00:04:17.649 --> 00:04:21.980
pointer variables are kind of special, so we need to implement additional

00:04:21.980 --> 00:04:26.060
pointer semantics to make this class act like a real pointer.

00:04:26.440 --> 00:04:28.620
And when I say pointer semantics,

00:04:28.620 --> 00:04:32.860
I mean things like dereference operator and the arrow operator.

00:04:32.860 --> 00:04:36.200
Dereference operator will simply the dereference the internal

00:04:36.200 --> 00:04:39.670
pointer to get to the pointed object, and it will return a

00:04:39.670 --> 00:04:42.060
reference to that place in memory.

00:04:42.440 --> 00:04:44.280
So, now that we defined it,

00:04:44.290 --> 00:04:48.840
we can do something like this. I can dereference a smart pointer and

00:04:48.850 --> 00:04:51.750
access the real member from the complex object.

00:04:52.640 --> 00:04:53.990
If I compile this,

00:04:54.000 --> 00:04:59.140
you can see that it works. Since we want to access the objects member,

00:04:59.150 --> 00:05:02.460
I would rather use the arrow operator to do that.

00:05:02.940 --> 00:05:07.560
But of course, to do that, we need to overload the arrow operator first.

00:05:08.440 --> 00:05:11.000
The arrow operator should return the raw pointer

00:05:11.000 --> 00:05:13.560
itself, that is all we need to do.

00:05:14.940 --> 00:05:16.260
Let's compile this.

00:05:17.540 --> 00:05:20.680
And as you can see, both operators are working,

00:05:20.690 --> 00:05:25.250
this smart pointer can now be used just like a regular raw pointer.

00:05:25.640 --> 00:05:30.170
But if you look closely into the implementation of this arrow operator,

00:05:30.170 --> 00:05:33.350
you might have some questions about how it works.

00:05:33.740 --> 00:05:37.760
The arrow operator simply returns the internal raw pointer.

00:05:39.140 --> 00:05:42.230
So instead of using this arrow operator directly,

00:05:42.240 --> 00:05:46.060
let's rewrite this statement to use it as a member function.

00:05:47.140 --> 00:05:50.650
We know that this expression will return a raw pointer.

00:05:51.040 --> 00:05:53.250
So, if this returns a pointer,

00:05:53.260 --> 00:05:56.810
how do we get the access to the actual member of the complex

00:05:56.810 --> 00:06:02.170
object? Shouldn't we dereference this pointer first and then use

00:06:02.170 --> 00:06:06.650
the dot operator? We could do that, but using the arrow operator

00:06:06.650 --> 00:06:08.460
like this will still work.

00:06:09.340 --> 00:06:11.450
This is because the legacy implementation of the

00:06:11.450 --> 00:06:13.860
arrow operator is kind of magical.

00:06:14.240 --> 00:06:18.540
It will cascade down until it reaches a raw pointer, and then

00:06:18.540 --> 00:06:22.530
it will do its usual thing, it will dereference a pointer and

00:06:22.540 --> 00:06:24.350
access the required member.

00:06:24.740 --> 00:06:26.620
So, once we call it here,

00:06:26.630 --> 00:06:30.950
the arrow operator will first check if the smart pointer has an overload.

00:06:31.340 --> 00:06:35.110
Since it has, it will call that overloaded function

00:06:35.110 --> 00:06:37.060
which will return a raw pointer.

00:06:37.440 --> 00:06:38.810
Once it has a pointer,

00:06:38.810 --> 00:06:43.160
it will again apply its usual behavior to the pointer itself.

00:06:43.940 --> 00:06:47.640
So, this statement is actually equal to this one,

00:06:47.650 --> 00:06:51.410
and that's why it works. Now that we know the general idea

00:06:51.410 --> 00:06:54.150
behind smart pointers, let's check out the available

00:06:54.150 --> 00:06:56.860
implementations from the standard library.
