Modern (Effective) C++ - std::unique_ptr

The article is hugely inspired by Item 18 of Effective Modern C++ by Scott Meyers. Here I will talk about smart pointers, particularly std::unique_ptr along with it's characteristics. To conclude, I have written a test program to back up the theory, so that you can test it on your own machine or online IDE.

The smart pointers namely the std::unique_ptr and std::shared_ptr are realizations of RAII ( Resource Allocation Is Initialization ) concept.
RAII is a technique where the programmer does not have to explicitly do and undo a action or set of actions on a resource( for example, creating and freeing memory, opening /closing database connections, locking and unlocking of the mutex etc).

In this contrived example, without using RAII


Notice that in each of the exit path, we need to explicitly free someInt variable by calling delete. Apart from the code duplication, which is violation of DRY design principle (Don't Repeat Yourself), there is a good chance that a new developer adding new logic with yet another exit path will forget to free someInt.

Hence with a few modifications of code, utilizing the RAII principle we get,


In this modified version of the code, we do not have to free someInt in every exit path. someInt is freed automatically when the closing brace of funcA is hit. someInt is also freed when any expected or unexpected exception is thrown.

std::unique_ptr and std::shared_ptr was inducted into C++ in C++11 . To use it in your code, include <memory> .

std::unique_ptr's size is the same as that of raw pointer ( Custom deleter functions can increase the size), while it's performance is very close to the raw pointer. Hence it can be used as drop in replacement for raw pointers ( except where raw pointers are copied or copy constructed), in most cases.

std::unique_ptr wraps around a raw pointer and has exclusive ownership of it. When std::unique_ptr, reaches the end of it's scope, it calls delete on the underlying pointer, thus calling it's destructor and freeing up the memory in heap. std::unique_ptr cannot be copy assigned or copy constructed.  However they can be moved or move constructed.


Deleter functions

std::unique_ptr takes an optional deleter function. If the custom deleter is not supplied, C++ uses a inbuilt default delete function object .
For instance,  std::unique_ptr<int> is internally represented as std::unique_ptr<int, std::default_delete<int> >

You can specify this deleter function as a function object, function or a lambda. In the simplest case, deleter function is used to record the deleted objects in a log file.

The deleter lambda function can increase the size of std::unique_ptr depending on the number of variables captured and used. 


I have run this code in Microsoft Visual Studio 2017.
We can make a few observations by running the code.
The size of a std::unique_ptr without a custom deleter and a std::unique_ptr with custom deleter which doesn't capture anything is the same.
We can capture the local variables in lambda either, by value(=) or by reference(&).

Here are two interesting sub-observations- 
  • We can capture all the local variables using  = or & in the lambda. But only the variables that are used in the lambda code, are captured in the closure. Hence the memory of the std::unique_ptr increases corresponding to the variables captured in the closure (not in the lambda)
  • If we capture a specific variable by mentioning the variable name in capture list, then it adds to the memory of the unique_ptr, irrespective of whether it is used or not.

These observations are taken on Microsoft Visual Studio 2017 ( MSVC 14.12), with optimization turned off. Do let me know in the comments if it is different in your compiler.

Feel free to run the above example in your own computer or online compiler like ideone.

Modern (Effective) C++ - std::unique_ptr Modern (Effective) C++ - std::unique_ptr Reviewed by zeroingTheDot on January 31, 2018 Rating: 5

No comments:

Powered by Blogger.