Smart Pointer: understanding unique_ptr

Share the Article

C++ smart pointer std::unique_ptr does everything which std::auto_ptr does, plus it also takes care of exclusive ownership of pointer. Clearly, a unique_ptr always exclusively owns the data it is pointing to. It must be noted that an explicit move operation is needed to transfer the ownership of data to another unique_ptr. (This point will be elaborated in a while)

Header file to be included to use unique_ptr:

#include <memory>

How to initialize unique_ptr ?

Any of the following 3 syntax can be used:

Using simple new Operator (from C++11)
std::unique_ptr<T> up1 (new T (constructor arguments));
Using make_unique function (from C++14)
std::unique_ptr<T> up1 = std::make_unique<T> (constructor arguments); auto up1 = std::make_unique<T> (constructor arguments);

Advantages of using make_unique instead of new:

  1. This perfect forwards the arguments
  2. This is more cleaner approach
  3. Takes care of exception safety

Basic implementation of std::make_unique

This is easy to write as shown below:

template <typename T, typename… Ts) std::unique_ptr make_unique(Ts&&... params) { return std::unique_ptr(new T(std::forward(params)...)); }

Transfer of ownership

The following command will not work because here the content of up1 is being assigned to up2. The content of up1 are exclusively owned and no other pointer can refer it. Therefore, compiler will throw an error:

std::unique_ptr<int> up2 = up1;

Note: the auto_ptr would have worked and would have make the up1 dangling.

Explicit transfer need to be done using move function, the following command will work:

std::unique_ptr<int> up2 = std::move(up1);

The up1 will become null and any further reference to up1 will result in undefined behavior. up2 will now exclusively own the resource.

How to print actual memory of actual data:

The memory address can be printed with get( ) function as shown in below example.

#include <iostream> //main header #include <memory> // for unique ptr using namespace std;//for namespace int main() //main function { auto up1 = std::make_unique<int>(); std::unique_ptr<int> up2 = std::move(up1); cout << up1.get() << endl; //Prints memory address of up1 cout << up2.get() << endl; //Prints memory address of up2 return 0; }

Output:

0 0x1943c20

Providing custom delete to unique_ptr

By default, resource destruction takes place via standard delete, but custom deleters can be specified. However, stateful deleters and function pointers as deleters increase the size of std::unique_ptr objects.

Example, with simple functor:

struct MySimpleDeleter { void operator()(int* ptr) const { printf("Deleting int pointer with functor!\n"); delete ptr; } }; std::unique_ptr<int, MySimpleDeleter> up1 (new int);

The output will be :

Deleting int pointer with functor!

Example with Lambda

auto lambdadeleter = [](int *a) { delete a; cout << ""Deleting int pointer with Lambda!" << endl; }; std::unique_ptr<int, decltype(lambdadeleter) > up1 (new int, lambdadeleter);

The output will be:

Deleting int pointer with Lambda!

Main Funda: smart pointer unique_ptr are better version of auto_ptr

Related Topics:

Parametrized constructor
Virtual Destructor & Pure Virtual Destructor
Smart Pointers: unique_ptr<T>
Diamond problem – Overhead of virtual base
Multiple Inheritance has multiple this pointers
Understanding multiple inheritance & virtual base classes
Understanding the copy constructor
What is move constructor ?
Understanding the order of calling constructors and destructors
What is an explicit constructor ?
Smart Pointers: shared_ptr <T> 
What happens when exception thrown from a constructor?
Why a destructor should never throw exception?
Compiler Generated Destructor is always non-virtual
Which member functions are generated by compiler in class?
Understanding array version of new[] & delete[]

Share the Article

Leave a Reply

Your email address will not be published. Required fields are marked *