# [Getting started in C++](./) - [Useful concepts and STL](./0-main.ipynb) - [Smart pointers](./6-SmartPointers.ipynb)
%% Cell type:markdown id: tags:
## Introduction
In short, **smart pointers** are the application of [RAII](./2-RAII.ipynb) to pointers: objects which handle more nicely the acquisition and release of dynamic allocation.
There are many ways to define the behaviour of a smart pointer (the dedicated chapter in [Modern C++ design](../bibliography.ipynb#Modern-C++-Design) is a very interesting read for this, especially as it uses heavily the template [policies](../4-Templates/5-MoreAdvanced.ipynb#Policies) to implement his):
* How the pointer might be copied (or not).
* When is the memory freed.
* Whether `if (ptr)` syntax is accepted
* ...
The STL made the choice of providing two (and a half in fact...) kinds of smart pointers (introduced in C++ 11):
***unique pointers**
***shared pointers** (and the **weak** ones that goes along with them).
One should also mention for legacy the first attempt: **auto pointers**, which were removed in C++ 17: you might encounter them in some libraries, but by all means don't use them yourself (look for *sink effect* on the Web if you want to know why).
By design all smart pointers keep the whole syntax semantic:
*`*` to dereference the (now smart) pointer.
*`->` to access an attribute of the underlying object.
Smart pointers are clearly a very good way to handle the ownership of a given object.
This does not mean they supersede entirely ordinary (often called **raw** or more infrequently **dumb**) pointers: raw pointers might be a good choice to pass an object as a function parameter (see the discussion for the third question in this [Herb Sutter's post blog](https://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/)). The raw pointer behind a smart pointer may be accessed through the `get()` method.
Both smart pointers exposed below may be constructed directly from a raw pointer; in this case they take the responsibility of destroying the pointer:
%% Cell type:code id: tags:
``` c++
#include<memory>
#include<iostream>
structFoo
{
~Foo()
{
std::cout<<"Destroy foo"<<std::endl;
}
};
{
Foo*raw=newFoo;
std::unique_ptr<Foo>unique(raw);// Now unique_ptr is responsible for pointer ownership: don't call delete
// on `raw`! Destructor of unique_ptr will call the `Foo` destructor.
}
```
%% Cell type:markdown id: tags:
## `unique_ptr`
This should be your first choice for a smart pointer.
The idea behind this smart pointer is that it can't be copied: there is exactly one instance of the smart pointer, and when this instance becomes out of scope the resources are properly released.
In C++ 11 you had to use the classic `new` syntax to create one, but C++ 14 introduced a specific syntax `make_unique`:
%% Cell type:code id: tags:
``` c++
#include<memory>
{
autoptr=std::make_unique<int>(5);
}
```
%% Cell type:markdown id: tags:
The parenthesis takes the constructor arguments.
The smart pointer can't be copied, but it can be moved:
%% Cell type:code id: tags:
``` c++
#include<memory>
{
autoptr=std::make_unique<int>(5);
autocopy=ptr;// COMPILATION ERROR: can't be copied!
std::cout<<"Beware as now there are no guarantee upon the content of ptr: "<<*ptr<<std::endl;// EXPECTED RUNTIME ISSUE!
returnEXIT_SUCCESS;
}
```
%% Cell type:markdown id: tags:
As usual with move semantics, beware in this second case: ptr is undefined after the `move` occurred... hence the segmentation fault you might have got.
### Usage to store data in a class
`std::unique_ptr` are a really good choice to store objects in a class, especially ones that do not have a default constructor.
You may always define an object directly as a data attribute without pointer indirection, but in this case you have to call explicitly the constructor of the data attribute with the `:` syntax before the body of the constructor (that's exactly what we did when we introduced composition [back in the inheritance notebook](../2-ObjectProgramming/6-inheritance.ipynb#CONTAINS-A-relationship-of-composition). By using a (smart) pointer, you loosen this constraint and may define the data attribute whenever you wish, not only at construction.
The underlying object may be accessed through reference or raw pointer; usually your class may look like:
%% Cell type:code id: tags:
``` c++
#include<string>
// Class which will be stored in another one through a `unique_ptr`
classContent
{
public:
Content(std::string&&text);// notice: no default constructor!
conststd::string&GetValue()const;
private:
std::stringtext_{};
};
```
%% Cell type:code id: tags:
``` c++
Content::Content(std::string&&text)
:text_(text)
{}
```
%% Cell type:code id: tags:
``` c++
conststd::string&Content::GetValue()const
{
returntext_;
}
```
%% Cell type:code id: tags:
``` c++
#include<memory>
classWithUniquePtr
{
public:
WithUniquePtr()=default;
voidInit(std::string&&text);// rather artificial here, but we want to point out it can be done anywhere and not just in constructor!
constContent&GetContent()const;
private:
//! Store `Content`object through a smart pointer.
assert(content_!=nullptr&&"Make sure Init() has been properly called beforehand!");
return*content_;
}
```
%% Cell type:markdown id: tags:
Doing so:
*`Content` is stored by a `unique_ptr`, which will manage the destruction in due time of the object (when the `WithUniquePtr` object will be destroyed).
*`Content` object might be manipulated through its reference; end-user don't even need to know resource was stored through a (smart) pointer:
%% Cell type:code id: tags:
``` c++
#include<iostream>
voidPrintContent(constContent&content)
{
std::cout<<content.GetValue()<<std::endl;
}
```
%% Cell type:code id: tags:
``` c++
{
autoobj=WithUniquePtr();// auto-to-stick syntax, to avoid most vexing parse.
obj.Init("My priceless text here!");
decltype(auto)content=obj.GetContent();
PrintContent(content);
}
```
%% Cell type:markdown id: tags:
(if you need a refresher about most vexing parse and auto-to-stick syntax, it's [here](../2-ObjectProgramming/3-constructors-destructor.ipynb#[WARNING]-How-to-call-a-constructor-without-argument)).
%% Cell type:markdown id: tags:
### Releasing a `unique_ptr`
To free manually the content of a `unique_ptr`:
* Use `release()` method:
%% Cell type:code id: tags:
``` c++
{
autoptr=std::make_unique<int>(5);
ptr.release();// Beware: `.` and not `->` as it is a method of the smart pointer class, not of the
// underlying class!
}
```
%% Cell type:markdown id: tags:
* Or assign `nullptr` to the pointer
%% Cell type:code id: tags:
``` c++
{
autoptr=std::make_unique<int>(5);
ptr=nullptr;
}
```
%% Cell type:markdown id: tags:
## `shared_ptr`
The philosophy of `shared_ptr` is different: this kind of smart pointers is fully copyable, and each time a copy is issued an internal counter is incremented (and decremented each time a copy is destroyed). When this counter reaches 0, the underlying object is properly destroyed.
As for `unique_ptr`, there is a specific syntax to build them (properly named `make_shared`...); it was introduced earlier (C++ 11) and is not just cosmetic: the compiler is then able to store the counter more cleverly if you use `make_shared` rather than `new` (so make it so!).
//< Notice the `.`: we access a method from std::shared_ptr, not from the type encapsulated
// by the pointer!
}
```
%% Cell type:markdown id: tags:
`shared_ptr` are clearly useful, but you should always wonder first if you really need them: for most uses a `unique_ptr` eventually seconded by raw pointers extracted by `get()` is enough.
There is also a risk of not releasing properly the memory is there is a circular dependency between two `shared_ptr`. A variation of this pointer named `weak_ptr` enables to circumvent this issue, but is a bit tedious to put into motion. I have written in [appendix](../7-Appendix/WeakPtr.ipynb) a notebook to describe how to do so.
%% Cell type:markdown id: tags:
## Efficient storage with vectors of smart pointers
*`std::vector` are cool, but the copy when capacity is exceeded might be very costly for some objects. Moreover, it forces you to provide copy behaviour to your classes intended to be stored in `std::vector`, which is not a good idea if you do not want them to be copied.
* An idea could be to use pointers: copy is cheap, and there is no need to copy the underlying objects when the capacity is exceeded. Another good point is that a same object might be stored in two different containers, and the modifications given in one of this is immediately "seen" by the other (as the underlying object is the same).
However, when this `std::vector` of pointers is destroyed the objects inside aren't properly deleted, provoking memory leaks.
The way to combine advantages without retaining the flaws is to use a vector of smart pointers:
%% Cell type:code id: tags:
``` c++
#include<array>
classNotCopyable
{
public:
NotCopyable(doublevalue);
~NotCopyable();
NotCopyable(constNotCopyable&)=delete;
NotCopyable&operator=(constNotCopyable&)=delete;
NotCopyable(NotCopyable&&)=delete;
NotCopyable&operator=(NotCopyable&&)=delete;
private:
std::array<double,1000>data_;
};
```
%% Cell type:code id: tags:
``` c++
NotCopyable::NotCopyable(doublevalue)
{
data_.fill(value);
}
```
%% Cell type:code id: tags:
``` c++
#include<iostream>
NotCopyable::~NotCopyable()
{
std::cout<<"Call to NotCopyable destructor!"<<std::endl;