Smart Pointers Notes
shared_ptr
Smart pointer implementation, which allows sharing a raw pointer among other shared_ptr’s instances.
When a shared pointer (sPtr) is created the block of memory (T) is allocated and a control block (ctrlBlock) is also created, which holds use_count
and weak_count
parameters. The sPtr contains two pointers: to the T and to the ctrlBlock. When use_count
== 0 the T is freed as no shared pointers own it. When weak_count
== 0 the ctrlBlock is destroyed as there is no point in keeping it alive. use_count = # of shared pointers
and weak_count = # of weak pointers + 1 if use_count > 0
.In case when use_count
== 0 and weak_count
> 0 the memory block T is freed but there is still at least one weak pointer to the T, so in order to correctly handle the dangling pointer access we keep the ctrlBlock alive.
Use of make_shared
allows allocate a continious block of memory for T and ctrlBlock in one call - that is why it’s fast.
or
or
or
But don’t do that:
because in this case the object obj
is allocated in stack and when ptr1
pointer will be deleting this object the program will crush.
The smart pointer expects that the object which pointer it holds is allocated in the heap.
If you want to share raw pointer, the safe method is:
In this case two smart pointers will be holding address of the object (ref count = 2). If one of the shared pointers exits it’s local
space it will be destroyied, reference counter will be decresed by 1, but the object obj
will exist and the other shared pointer will
be holding it’s address.
Not safe method is to let several shared pointers hold object’s adress through the raw pointer obj
:
In this case reference counters will be unique for each shared pointers and if one of the shared pointers leaves it’s locas space (1)
it will trigger dectructor for obj
object. But the other pointer still thinks that it’s holding adress of some object and any attempt
to do something with it (2) or leaving it’s local space (3) will cause an exception.
Shared pointers created with the make_shared
call use default deleter - the class destructor, to destroy allocated object when the ref counter is 0. However, we can pass a custom deleter to the shared pointer constructor:
This may be helpfull if we allocate an array of of objects and pass it to the shared pointer. Before C++17 shared pointers that own a pointer to an array by default will call delete
not delete[]
.
In some cases use of shared_ptr can cause Circular dependency issues
. Consider an example:
In this case objects tom
and casey
will be created but they won’t be destroyed, because reference counters will be = 2 and after leaving local space ref counters will not be set to 0. To avoid this issue we need to use weak_ptr
.
shared_ptr and multithreading
Reference counter is atomic mechanism so in multithreaded environment the number of shared pointers holding an object will be correctly calculated. Thread safe operations on the object will work ok (e.g. reading a value). BUT if any object modifying operation is used we need to make them thread safe with mutex.
Consider an example when we have a global shared_ptr and two functions:
Approximate impl of shared_ptr:
If one thread starts copying globalSharedPtr (read) and another reseting (write), then it’s possible to get race conditions over pointer/counter variables.
weak_ptr
Smart pointer implementation, which allows sharing a raw pointer among other shared_ptr’s instances and it doesn’t increment reference counter. Weak pointers complement Shared pointers.
To get acces to the object the weak_ptr
should be transformed into shared_ptr
:
Main use is to fix Circular dependency issues
:
unique_ptr
Smart pointer implementation, which solely holds a raw pointer of an object! It implements move semantic
-
In order to pass a pointer from one unique_ptr to another we need to use std::move
:
We can get a shared pointer from a unique one:
After that uptr1
will be empty and posession of the objects address went to shared
.
We can’t get a weak_ptr
from unique_ptr
(because weak_ptr
doesn’t inform that it holds a pointer)!
In contrast to the shared_ptr
unique pointers correctly work by default with pointers to arrays starting from C++11:
auto_ptr - is not recomended to use
First attempt to standartize smart pointers released in C++98. It implements move semantic
through copy constructor and redefined operator=. As a result it’s very error prone. Example:
Trying to dereference the auto_p will cause program crash because when some_func(auto_p)
is called the copy constructor of std::auto_ptr
will move pointer that auto_p holds to the some_func
parameter - auto_p is empty. Fix it with:
One possible use case:
Both MyChild
and MyClass
object will be deleted (even without virtual destructors).