How to create and use unique pointer in C++?

In this tutorial, you will learn “how to create and use a unique pointer in C++”. This tutorial will be specific on unique pointers, so the primary pre-requisite of this tutorial is that you should have basic knowledge about pointers and smart pointers.

If you don’t have the basic knowledge of pointers and smart pointers, you should read the below-mentioned articles before reading this article.

 

What is unique_ptr?

A unique pointer is an object that owns another object and manages that other object through a pointer. The unique pointer has exclusive ownership of the object it points to. This means  unique_ptr does not share its pointer with any other unique_ptr. It cannot be copied to another unique_ptr.

The unique_ptr can only be moved. It means that the ownership of the memory resource is transferred to another unique_ptr and the original unique_ptr no longer owns it.

Let’s understand unique_ptr with an example, suppose ptr is an object of the unique pointer that stores a pointer to a second object Test. The object ptr will dispose of Testwhen ptr is itself destroyed. In this context, ptr is said to own Test.

unique pointer C++ with example

 

Syntax of unique_ptr:

//Since C++11

(1) template< class T,class Deleter = std::default_delete<T> > class unique_ptr;


(2) template <class T,class Deleter> class unique_ptr<T[], Deleter>;

Where,

1. It manages a single object (e.g. allocated with new).

2. It manages a dynamically-allocated array of objects (e.g. allocated with new[]).

 

How to create an instance of unique_ptr?

The below-mentioned example shows how to create instances of unique_ptr.

/*
 Object ptr owns dynamically allocated int
*/ 

std::unique_ptr<int> ptr(new int);

 

Create unique_ptr in C++

 

Remark: You can also create a unique pointer with std::make_unique (since C++14). See the below expression.

// Create a new unique_ptr object.

auto ptr = make_unique<int>();

 

The dynamically allocated object is destroyed when the created unique pointer object is destroyed. See the below example code.

void foo()
{
  unique_ptr<int> ptr (new int);

} <<---- ptr is destructed outside of this of curly braces.

The object ptr is a stack-allocated object. When control goes out of the scope, it destroys automatically and also destroys the dynamically allocated unnamed int object using the associated deleter.

unique pointers C++ aticleworld

 

Strict ownership of unique pointers:

The unique pointer object has exclusive ownership with its pointed object. It does not share its ownership with any other unique pointers. It means you can not copy a unique pointer. But however you can only transfer its ownership to another unique pointer, we will understand this concept with the help of a programming example.

The following example shows that we can not assign a unique pointer to another unique pointer.

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    /*
     Object ptr owns dynamically
     allocated unnamed int object.
    */
    unique_ptr<int> ptr1 (new int);


    // Error: can't copy unique_ptr
    unique_ptr<int> ptr2 = ptr1;

    return 0;
}

Output:Compiler error.

 

Remark: We can create an empty unique pointer.

// ptr is empty pointer, contains null pointer

unique_ptr<int> ptr;

 

Different Operations supported by unique_ptr:

You will see different operations supported by the unique pointers with the help of programming examples.

Get the stored pointer:

We can easily get the raw pointer which is stored by the unique pointer with the help of get(). It returns a pointer to the managed object or nullptr if no object is owned. See the below example,

#include <iostream>
#include <memory>
using namespace std;


class Test
{
public:
    void print()
    {
        cout << "Test::print()" << endl;
    }
};


int main()
{
    /*
    Create an unique pointer
    object that store the pointer to
    the Test object
    */
    unique_ptr<Test> ptr(new Test);

    //returns a pointer to the managed object
    cout << "ptr.get() = "<< ptr.get() << endl;

    //Calling print function using the
    //unique pointer
    ptr->print();

    cout<<"\nOperation with raw pointer\n\n";

    auto ptrObj = ptr.get();

    //print raw pointer
    cout << "ptrObj = "<< ptrObj << endl;

    //Calling print function using the
    //raw pointer
    ptrObj->print();

    return 0;
}

Output:

ptr.get() = 0xf81700
Test::print()

Operation with raw pointer

ptrObj = 0xf81700
Test::print()

 

Remark: Do not explicitly delete the raw pointer because it is managed by the unique pointer. You might get UB.

 

Resetting a unique_ptr:

The reset() member function replaces the managed object. It takes ownership of the newly created objects (if any) and if the old pointer was non-empty, deletes the previously managed object.

Case 1: Old pointer is empty( null_ptr)

Takes ownership of the object and does not call the deleter because the old pointer is empty

#include <iostream>
#include <memory>
using namespace std;

int main ()
{
    //create empty unique pointer
    std::unique_ptr<int> ptr;

    //returns a nullptr because empty object
    cout << "ptr.get() = "<< ptr.get() << endl;

    /*
     Takes ownership of pointer.
     Does not call deleter because old pointer is null (empty)
    */
    ptr.reset (new int);

    //assign a value and printing the same
    *ptr = 5;
    cout << *ptr << '\n';

    return 0;
}

Output:

ptr.get() = 0
5

 

Case 2: Old pointer is not empty( already managing an object)

Takes ownership of the new object and call the deleter to destroy old managed object.

#include <iostream>
#include <memory>
using namespace std;

int main ()
{
    //create empty unique pointer
    std::unique_ptr<int> ptr(new int);

    //returns pointer to the old managed object
    cout << "Old ptr.get() = "<< ptr.get() << endl;

    /*
     Takes ownership of pointer.
     Call deleter because old pointer is not empty.
    */
    ptr.reset (new int);

    //returns pointer to the newly managed object
    cout << "New ptr.get() = "<< ptr.get() << endl;

    return 0;
}

Output:

Old ptr.get() = 0x701700
New ptr.get() = 0x701710

 

Case 3: Delete the managed object

You can also use reset to only destroy the already managed object.

#include <iostream>
#include <memory>
using namespace std;

int main ()
{
    //create empty unique pointer
    std::unique_ptr<int> ptr(new int);

    //returns pointer to the old managed object
    cout << "ptr.get() = "<< ptr.get() << endl;

    // deletes managed object
    ptr.reset();

    //returns pointer
    cout << "ptr.get() = "<< ptr.get() << endl;

    return 0;
}

Output:

ptr.get() = 0xf91700
ptr.get() = 0

 

Transferring the ownership of unique_ptr object:

The following example shows how to create unique_ptr instances and how to transfer the ownership to other unique pointers.

#include <iostream>
#include <memory>
using namespace std;

class Test
{
public:
    void print()
    {
        cout << "Test::print()" << endl;
    }
};

int main()
{
    /*
    Create an unique pointer
    object that store the pointer to
    the Test object
    */
    unique_ptr<Test> ptr1(new Test);

    //Calling print function using the
    //unique pointer
    ptr1->print();

    //returns a pointer to the managed object
    cout << "ptr1.get() = "<< ptr1.get() << endl;

    /*
    transfers ptr1 ownership to ptr2 using the move.
    Now ptr1 don't have any ownership
    and ptr1 is now in a 'empty' state, equal to `nullptr`
    */
    unique_ptr<Test> ptr2 = move(ptr1);
    ptr2->print();

    //Prints return of pointer to the managed object
    cout << "ptr1.get() = "<< ptr1.get() << endl;
    cout << "ptr2.get() = "<< ptr2.get() << endl;

    return 0;
}

Output:

unique pointers C++

 

In the above code, you can see that using the move we are transferring the ownership of ptr1 to ptr2. The below image will help you to understand the concept.

unique pointer with move

 

Releasing the associated raw pointer:

Calling the release() member function on the unique_ptr object releases the ownership of the managed object(if any). It returns a pointer to the managed object or nullptr if there was no managed object.

Note: Its return value is the value get() had at the start of the call to release().

After calling the release() get()returns the nullptr. The caller is responsible for deleting the raw pointer of the allocated object.

#include <iostream>
#include <memory>
using namespace std;


int main()
{
    std::cout << "Creating new int...\n";
    std::unique_ptr<int> ptr(new int);

    //returns a pointer to the managed object
    cout << "ptr.get() = "<< ptr.get() << endl;

    std::cout << "\nrelease created int...\n\n";
    int* intPtr = ptr.release();

    std::cout << "int is no longer owned by unique_ptr...\n";
    //returns null
    cout << "ptr.get() = "<< ptr.get() << endl;

    cout << "Raw pointer:- intPtr = "<< intPtr << endl;

    //delete the object
    delete intPtr;
}

Output:

Creating new int...
ptr.get() = 0xe61700

release created int...

int is no longer owned by unique_ptr...
ptr.get() = 0
Raw pointer:- intPtr = 0xe61700

 

Swapping the associated raw pointer:

Calling the swap() swaps the managed objects and associated deleters of *this  with another unique_ptr object.

Note: get_deleter() must be swappable and does not throw an exception under the swap.

#include <iostream>
#include <memory>
using namespace std;


int main()
{
    std::unique_ptr<int> ptr1(new int(27));
    std::unique_ptr<int> ptr2(new int(6));

    cout << "print pointers owned by unique_ptrs...\n";
    //returns a pointer to the managed object
    cout << "ptr1.get() = "<< ptr1.get() << endl;
    cout << "ptr2.get() = "<< ptr2.get() << endl;

    cout << "print value the which owned by unique_ptrs...\n";
    cout << "*ptr1 = "<< *ptr1 << endl;
    cout << "*ptr2 = "<< *ptr2 << endl;

    cout << "Calling swap on unique_ptrs...\n";
    ptr1.swap(ptr2);

    cout << "AFTER SWAP:- print pointers owned by unique_ptrs...\n";
    //returns a pointer to the managed object
    cout << "ptr1.get() = "<< ptr1.get() << endl;
    cout << "ptr2.get() = "<< ptr2.get() << endl;

    cout << "AFTER SWAP:- print value the which owned by unique_ptrs...\n";
    cout << "*ptr1 = "<< *ptr1 << endl;
    cout << "*ptr2 = "<< *ptr2 << endl;

    return 0;
}

Output:

Swap with unique_ptr C++

 

Checking empty unique_ptr in C++:

We can check whether a unique_ptr is associated with an object or not.

#include <iostream>
#include <memory>
using namespace std;


int main ()
{
    unique_ptr<int> ptr1;
    unique_ptr<int> ptr2 (new int(27));

    //Check first unique pointer
    if (ptr1)
    {
        std::cout << "ptr1 points to " << *ptr1 << '\n';
    }
    else
    {
        std::cout << "ptr1 is empty\n";
    }

    //Check second unique pointer
    if (ptr2)
    {
        std::cout << "ptr2 points to " << *ptr2 << '\n';
    }
    else
    {
        std::cout << "ptr2 is empty\n";
    }
    return 0;
}

Output:

ptr1 is empty
ptr2 points to 27

 

 

How to Pass to a unique pointer in function:

The following examples show how to create unique_ptr instances and pass them between functions.

Pass the unique smart pointer by reference:

Examples demonstrate how we can pass a unique pointer as a reference in a function. The foo() function is taking a unique pointer as an argument and uses it to print the value of the class attribute.

#include <iostream>
#include <memory>
using namespace std;


class Test
{
public:
    Test(int val):m_val(val)
    {
        cout<<"Object Created\n";
    }
    ~Test()
    {
        cout<<"Object Destroyed\n";
    }
    //getter function
    int get()
    {
        return m_val;
    }
    //setter function
    int set(int x)
    {
        m_val = x;
    }
private:
    int m_val;
};


//function printing the value
void foo(unique_ptr<Test> & arg)
{
    cout << arg->get() << endl;
}

int main()
{
    unique_ptr<Test> ptr = unique_ptr<Test>(new Test(27));
    
    //passing unique pointer in function foo()
    foo(ptr);
    
    return 0;
}

Output:

Object Created
27
Object Destroyed

 

Move the unique smart pointer into the function argument:

#include <iostream>
#include <memory>
using namespace std;


class Test
{
public:
    Test(int val):m_val(val)
    {
        cout<<"Object Created\n";
    }
    ~Test()
    {
        cout<<"Object Destroyed\n";
    }
    //getter function
    int get()
    {
        return m_val;
    }
    //setter function
    int set(int x)
    {
        m_val = x;
    }
private:
    int m_val;
};


//function printing the value
void foo(unique_ptr<Test> arg)
{
    cout << arg->get() << endl;
}

int main()
{
    unique_ptr<Test> ptr = unique_ptr<Test>(new Test(27));

    //move the unique pointer in function foo()
    foo(move(ptr));
    if (!ptr) cout<< "ptr is empty."; // true: ptr is empty.
    
    return 0;
}

Output:

Object Created
27
Object Destroyed
ptr is empty.

 

 

Return unique pointer from a function:

You can return a unique_ptr from a function. See the below code.

#include <iostream>
#include <memory>
using namespace std;


//function printing the value
unique_ptr<int> foo()
{
    unique_ptr<int> ptr = unique_ptr<int>(new int(27));

    /* you can also use below expression
     auto ptr = make_unique<int>(27);
     */
    return ptr;
}

int main()
{
    unique_ptr<int> ptr  = foo();

    // true: ptr has an object.
    if (ptr)
    {
        cout<< "ptr owned an object.\n";
        cout <<"*ptr = " << *ptr;
    }

    return 0;
}

Output:

ptr owned an object.
*ptr = 27

 

 

Use unique pointer with vector:

The below example shows how to create unique_ptr instances and use them in a vector.

#include <iostream>
#include <memory>
#include <vector>
using namespace std;

int main()
{
    vector<unique_ptr<int>> ptr;

    // Create a few new unique_ptr<int> instances
    // and add them to vector using the move semantics.
    ptr.push_back(make_unique<int>(6));
    ptr.push_back(make_unique<int>(27));
    ptr.push_back(make_unique<int>(24));
    ptr.push_back(make_unique<int>(8));


    for (int i = 0; i < ptr.size(); i++)
    {
        cout << *ptr[i] <<endl;
    }

    return 0;
}

Output: 6, 27, 24, 8

 

How to initialize a unique_ptr if it is a class member?

The below code shows how to initialize a unique_ptr that is a class member.

class MyTest
{
public:
    void doSomething()
    {
        cout << "Share this post\n";
    }
};

class Test
{
private:
    // Test owns the unique_ptr.
    unique_ptr<MyTest> m_ptr;
public:
    /* Initialize by using make_unique 
       with MyTest default constructor.
    */
    Test() : m_ptr (make_unique<MyTest>())
    {
    }

    void callMytestFun()
    {
        m_ptr->doSomething();
    }
};

 

Recommended Articles for you:

References:
Dynamic memory management.

Leave a Reply

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