Hello again!
In this video, we are going to start looking at smart pointers.
First, let's quickly remind ourselves about traditional pointers, which are definitely not smart!
The traditional pointer is inherited from C. It is a built-in type. And variables which are pointers
store numbers, which represent an address in memory.
This memory could belong to a variable on the stack.
For example, if we are passing a function argument by address. Or it could represent memory which has
been allocated on the heap with new, in which case we have to manage the memory, and remember to release
it by calling delete.
Typically this is when you have a variable-length array. And in C and older versions of C++,
this was the only way to create an array,
if you did not know the size at compile time, or if the array was too big for the stack. And we can
also use traditional pointers for polymorphism: we can have a pointer to a base class which actually
points to an object of a derived class.
And when we call virtual functions through this pointer, we get the version from the derived class.
There are many drawbacks to traditional pointers.
They are a common sources of bugs.
The main problem is, there is no concept of ownership.
Nobody actually owns a pointer.
Any code can re-assign a pointer overwrite it, release the memory, or set it to null.
And also there is no clear indication of whether a pointer is still in use.
So you can end up releasing memory twice, which will cause a memory error, probably a crash, or not
releasing at all, in which case you get a memory leak.
Fortunately, C++11 now means that you do not need to use traditional pointers very often.
If you are working with stack memory, we can pass by reference, instead of passing by address.
If we want a variable-sized array, we can use the library vector class.
If we are working with heap memory, we can use these "smart" pointers, and if we want dynamic binding,
we can use smart pointers again.
Or we could use references.
So, what are smart pointers? Smart pointers are classes which encapsulates allocated memory.
The class has a private data member, which is a pointer to this allocated memory.
The smart pointer object has ownership of the pointer and the allocated memory, so nothing from outside
the class can interfere with it.
The only way to access this memory is through public member functions of the class. And things like pointer
arithmetic are not allowed.
Smart points are implemented using the RAII idiom.
The memory is allocated in the construction and released in the destructor.
So this guarantees that the memory is always released, either at the end of scope, or if an exception is thrown.
Smart pointers will save you a lot of work. Both when coding, because you do not need to keep track
of all this memory and who is using it.
And also when debugging, because you will not have so many crashes to debug!
So let's look at the smartpointers which C++ offers.
The first attempt was auto_ptr, in C++98.
So this had the basic idea of encapsulating the memory inside the class.
Unfortunately, it had some rather weird semantics.
If you copied an auto_ptr,
the copied-from object had its pointer set to null.
So in effect, the memory was transferred to the object you were copying into.
And this caused a lot of problems because, as programmers, we tend to assume that if we copy from something,
it is not going to be modified.
And there is a lot of code which assumes that copied-from objects are still valid.
For example, if you store auto_ptrs in containers, or use them in generic code, then you could get unpredictable
behaviour.
So this was a bit of a flop, really, and it has now been removed from the language.
C++11 has unique_ptr, which is a "fixed" version of auto_ptr, if you like.
It does everything that auto_ptr should have done.
The reason why it could not be done earlier, is because move semantics only came in in C++11.
So with a unique_ptr, it cannot be copied or assigned to, but the memory can be transferred from
one unique_ptr object to another one.
So this solves the problem with auto_ptr: if we want the memory to be transferred from one object
to another, we need to do that explicitly, by invoking the move constructor.
So we needed to put the copied-from object inside a call to move.
And then when you look through the code, it will be very obvious that this object has been moved from,
and is no longer valid.
It is not possible to accidentally transfer the memory, like it was with auto_ptr.
If you try to copy a unique_ptr, then the code will not compile.
There's one other main smart pointer in C++11, which is the shared_ptr.
And as the name suggests, this can share the allocated memory with other shared_ptr objects.
But it is still encapsulated inside the class, and can only be accessed via member functions.
The sharing is managed by something called "reference counting".
If you are not sure about that, then I have a video on that, later on.
This means that the memory is only released, when there are no more objects which are sharing it.
So the last object which is destroyed will release the memory.
So this is quite similar to a "garbage-collected" object in other languages.
The shared_ptr has more overhead than
unique_ptr. It takes longer to do operations.
So you should only use this if you need the extra features.
Okay, so that is it for this video.
I will see you next time.
But until then, keep coding!