WEBVTT

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

00:01.020 --> 00:04.050
In this video, we are going to start looking at smart pointers.

00:05.310 --> 00:10.260
First, let's quickly remind ourselves about traditional pointers, which are definitely not smart!

00:11.340 --> 00:17.310
The traditional pointer is inherited from C. It is a built-in type. And variables which are pointers

00:17.610 --> 00:20.610
store numbers, which represent an address in memory.

00:23.250 --> 00:25.860
This memory could belong to a variable on the stack.

00:26.160 --> 00:32.280
For example, if we are passing a function argument by address. Or it could represent memory which has

00:32.280 --> 00:37.560
been allocated on the heap with new, in which case we have to manage the memory, and remember to release

00:37.560 --> 00:38.580
it by calling delete.

00:39.900 --> 00:45.510
Typically this is when you have a variable-length array. And in C and older versions of C++,

00:45.930 --> 00:47.740
this was the only way to create an array,

00:48.120 --> 00:54.060
if you did not know the size at compile time, or if the array was too big for the stack. And we can

00:54.060 --> 00:59.880
also use traditional pointers for polymorphism: we can have a pointer to a base class which actually

00:59.880 --> 01:02.580
points to an object of a derived class.

01:03.180 --> 01:08.700
And when we call virtual functions through this pointer, we get the version from the derived class.

01:13.870 --> 01:15.980
There are many drawbacks to traditional pointers.

01:16.000 --> 01:17.260
They are a common sources of bugs.

01:17.740 --> 01:20.050
The main problem is, there is no concept of ownership.

01:20.530 --> 01:22.060
Nobody actually owns a pointer.

01:22.330 --> 01:27.880
Any code can re-assign a pointer overwrite it, release the memory, or set it to null.

01:28.870 --> 01:32.360
And also there is no clear indication of whether a pointer is still in use.

01:32.770 --> 01:38.920
So you can end up releasing memory twice, which will cause a memory error, probably a crash, or not

01:38.920 --> 01:41.650
releasing at all, in which case you get a memory leak.

01:44.720 --> 01:49.760
Fortunately, C++11 now means that you do not need to use traditional pointers very often.

01:50.480 --> 01:54.770
If you are working with stack memory, we can pass by reference, instead of passing by address.

01:55.460 --> 01:59.600
If we want a variable-sized array, we can use the library vector class.

02:00.320 --> 02:05.870
If we are working with heap memory, we can use these "smart" pointers, and if we want dynamic binding,

02:06.170 --> 02:07.850
we can use smart pointers again.

02:08.150 --> 02:09.560
Or we could use references.

02:13.370 --> 02:18.650
So, what are smart pointers? Smart pointers are classes which encapsulates allocated memory.

02:19.280 --> 02:24.050
The class has a private data member, which is a pointer to this allocated memory.

02:25.130 --> 02:30.590
The smart pointer object has ownership of the pointer and the allocated memory, so nothing from outside

02:30.590 --> 02:31.880
the class can interfere with it.

02:33.230 --> 02:39.290
The only way to access this memory is through public member functions of the class. And things like pointer

02:39.290 --> 02:40.730
arithmetic are not allowed.

02:43.630 --> 02:47.140
Smart points are implemented using the RAII idiom.

02:47.470 --> 02:52.120
The memory is allocated in the construction and released in the destructor.

02:53.420 --> 02:59.060
So this guarantees that the memory is always released, either at the end of scope, or if an exception is thrown.

03:00.350 --> 03:05.420
Smart pointers will save you a lot of work. Both when coding, because you do not need to keep track

03:05.420 --> 03:07.130
of all this memory and who is using it.

03:07.640 --> 03:11.090
And also when debugging, because you will not have so many crashes to debug!

03:14.600 --> 03:17.390
So let's look at the smartpointers which C++ offers.

03:17.780 --> 03:20.930
The first attempt was auto_ptr, in C++98.

03:21.530 --> 03:24.650
So this had the basic idea of encapsulating the memory inside the class.

03:25.280 --> 03:27.740
Unfortunately, it had some rather weird semantics.

03:28.580 --> 03:30.470
If you copied an auto_ptr,

03:31.160 --> 03:33.890
the copied-from object had its pointer set to null.

03:34.130 --> 03:38.000
So in effect, the memory was transferred to the object you were copying into.

03:39.200 --> 03:43.580
And this caused a lot of problems because, as programmers, we tend to assume that if we copy from something,

03:43.880 --> 03:45.410
it is not going to be modified.

03:45.770 --> 03:49.780
And there is a lot of code which assumes that copied-from objects are still valid.

03:51.520 --> 03:57.250
For example, if you store auto_ptrs in containers, or use them in generic code, then you could get unpredictable

03:57.250 --> 03:57.730
behaviour.

03:58.330 --> 04:02.230
So this was a bit of a flop, really, and it has now been removed from the language.

04:05.100 --> 04:10.310
C++11 has unique_ptr, which is a "fixed" version of auto_ptr, if you like.

04:10.320 --> 04:12.660
It does everything that auto_ptr should have done.

04:13.350 --> 04:18.120
The reason why it could not be done earlier, is because move semantics only came in in C++11.

04:18.810 --> 04:24.570
So with a unique_ptr, it cannot be copied or assigned to, but the memory can be transferred from

04:24.570 --> 04:27.090
one unique_ptr object to another one.

04:29.750 --> 04:35.210
So this solves the problem with auto_ptr: if we want the memory to be transferred from one object

04:35.210 --> 04:39.470
to another, we need to do that explicitly, by invoking the move constructor.

04:39.800 --> 04:43.220
So we needed to put the copied-from object inside a call to move.

04:43.850 --> 04:48.440
And then when you look through the code, it will be very obvious that this object has been moved from,

04:48.440 --> 04:49.820
and is no longer valid.

04:51.470 --> 04:55.040
It is not possible to accidentally transfer the memory, like it was with auto_ptr.

04:55.790 --> 04:58.730
If you try to copy a unique_ptr, then the code will not compile.

05:02.860 --> 05:07.540
There's one other main smart pointer in C++11, which is the shared_ptr.

05:08.020 --> 05:12.850
And as the name suggests, this can share the allocated memory with other shared_ptr objects.

05:13.180 --> 05:18.010
But it is still encapsulated inside the class, and can only be accessed via member functions.

05:19.420 --> 05:22.810
The sharing is managed by something called "reference counting".

05:24.550 --> 05:27.460
If you are not sure about that, then I have a video on that, later on.

05:28.000 --> 05:32.470
This means that the memory is only released, when there are no more objects which are sharing it.

05:32.740 --> 05:35.890
So the last object which is destroyed will release the memory.

05:39.570 --> 05:43.830
So this is quite similar to a "garbage-collected" object in other languages.

05:45.000 --> 05:47.280
The shared_ptr has more overhead than

05:47.280 --> 05:50.190
unique_ptr. It takes longer to do operations.

05:50.610 --> 05:53.190
So you should only use this if you need the extra features.

05:53.970 --> 05:55.400
Okay, so that is it for this video.

05:55.830 --> 05:56.640
I will see you next time.

05:56.640 --> 05:58.800
But until then, keep coding!
