What is unique_ptr in C++?

In this article, we will look into a Smart Pointer unique_ptr in C++. It provides an easier way of memory management. Smart pointers are an integral part of modern C++ as they help prevent memory leaks and ensure that memory is managed automatically.

What is a unique_ptr?

A unique_ptr is a type of smart pointer provided by the C++ Standard Library that is designed to manage the memory of a dynamically allocated memory. It holds the exclusive ownership of the memory it points to, meaning there can be no other unique_ptr pointing to the same memory at the same time. This exclusive ownership is the first way in which unique_ptr simplifies memory management.

How does unique_ptr help?

The benefits of using unique_ptr are as follows,

  1. Exclusive Ownership: At any given time, there can only be one unique_ptr object managing a specific block of memory. This eliminates the complexities of handling multiple pointers for the same memory resource, reducing the chances of programming errors.
  2. Automatic Memory Deallocation: When a unique_ptr goes out of scope, the memory it manages is automatically deallocated. This is extremely beneficial as it removes the burden from programmers to manually delete memory, which is error-prone and can lead to memory leaks if forgotten.

Avoiding the delete Operator

Traditionally, dynamic memory in C++ is allocated using the new operator and deallocated using delete. However, with unique_ptr, the need to directly use these operators can be avoided, thus allowing the C++ runtime system to manage memory for us. This can be achieved by using the std::make_unique function introduced in C++14, which creates a unique_ptr that manages a new object.

Traditional Memory Management

Traditional approaches of memory management in C++ are as follows,

  • Stack Variables:

Initially, managing memory via stack variables is straightforward. You create a variable, use it, and once it goes out of scope, the system cleans it up. There’s no need for active management, which is ideal and error-free.

int main()
{
    {
        // Memory for variable is allocated on stack
        int number = 10;
    }
    // Variable number, goes out of scope 
    // and memory is cleaned up from stack
    return 0;
}

  • Raw Pointers and Heap Memory:

Alternatively, raw pointers have been used for dynamic memory allocation on the heap. With raw pointers, developers need to be meticulous about deallocating memory with the delete operator once an object is no longer needed. Failure to do so results in memory leaks, a common issue in C++ development.

#include <iostream>
#include <memory>

int main()
{
    int * ptr = new int(40);

    std::cout<< "value: " << *ptr << std::endl;

    delete ptr;
    ptr = NULL;

    return 0;
}

Smart pointers, like unique_ptr, are designed to mimic the ease of stack variables while providing the flexibility of heap allocation. When you employ a smart pointer, it assumes the role of a caretaker for the allocated memory, ensuring that once its job is done, it cleans up after itself, leaving no memory leaks in its wake.

How to use unique_ptr in C++ ?

To utilize unique_ptr, you must include the <memory> header file at the beginning of your C++ program:

#include <memory>

unique_ptr can also be created and assigned heap memory directly upon its declaration:

// Directly managing a new int object
std::unique_ptr<int> ptrObj(new int(4)); 

Here we have dynamically allocated memory on the heap to store an integer value and passed the pointer pointing to this memory to a unique_ptr. Now, the unique_ptr object is responsible for this memory, and you don’t need to manually delete this allocated memory.

When the unique_ptr object ptrObj goes out of scope, it will automatically delete the memory linked with the unique_ptr object.

Initializing unique_ptr with nullptr

If you’re not ready to assign memory to unique_ptr immediately, you can initialize it with nullptr and assign it later:

std::unique_ptr<int> ptrObj(nullptr);

// Later in the code
ptrObj.reset(new int()); // Now managing a new int object

Utilizing unique_ptr Like Raw Pointers

Using unique_ptr feels familiar as you can utilize it similarly to raw pointers. Here’s how you can manipulate the underlying object or primitive:

#include <iostream>
#include <memory>

int main()
{
    std::unique_ptr<int> intPtr(new int(4)); 

    // Assigning value to the managed integer
    *intPtr = 42; 

    // Dereferencing to print the value
    std::cout << *intPtr; 

    return 0;
}

Output:

42

And if you need to obtain the raw pointer for some operations, especially when interacting with APIs that require raw pointers, unique_ptr provides a get() method:

int* rawIntPtr = intPtr.get(); // Acquires the raw pointer
// Remember: don't use rawIntPtr for memory management!

Remember, the use of get() should be limited and never for managing the memory that unique_ptr is responsible for.

RAII and unique_ptr in C++

RAII, which stands for Resource Acquisition Is Initialization, is a core concept in C++ that ensures resources are properly released when they are no longer needed. Smart pointers, like unique_ptr, are a perfect example of RAII in action. They manage the lifecycle of dynamically allocated memory, ensuring automatic deallocation when the smart pointer goes out of scope. This pattern helps prevent memory leaks and dangling pointers, common issues in manual memory management.

Let’s look at a practical example using unique_ptr:

#include <iostream>
#include <string>
#include <memory>

class Tweet
{
private:
    std::string text; // The content of the tweet
    int viewCount;    // Number of times the tweet has been viewed
    int likeCount;    // Number of likes on the tweet

public:
    // Constructor to initialize the tweet object with text, view count, and like count
    Tweet(const std::string &txt, int views, int likes)
        : text(txt), viewCount(views), likeCount(likes)
    {
        std::cout<< "*** Tweet::Constructor ***\n";
    }

    // Display function to print the details of the tweet
    void display() const
    {
        std::cout << "Tweet: " << text << "\n";
        std::cout << "Views: " << viewCount << "\n";
        std::cout << "Likes: " << likeCount << "\n";
    }

    ~Tweet()
    {
        std::cout<< "*** Tweet::Destructor ***\n";
    }
};

int main()
{
    // Wrapping the raw pointer with a unique_ptr
    // unique_ptr now exclusively manages the Tweet
    std::unique_ptr<Tweet> tweetPtr(new Tweet("Good Morning", 2000, 31));

    tweetPtr->display();

    return 0;
}

Output:

*** Tweet::Constructor ***
Tweet: Good Morning
Views: 2000
Likes: 31
*** Tweet::Destructor ***

In the code above, tweetPtr is an instance of unique_ptr managing the lifecycle of a Tweet object. Here’s how unique_ptr upholds the principles of RAII:

  1. Resource Acquisition: The Tweet object is dynamically allocated with new, and its pointer is immediately passed to tweetPtr. The acquisition of the resource and its initialization with a managing entity are simultaneous.
  2. Resource Management: As soon as unique_ptr takes control, it becomes the sole manager of the Tweet object’s memory. The original raw pointer (rawTweet) is set to nullptr to prevent accidental deletion or access, reinforcing that tweetPtr now has exclusive management over the object.
  3. Resource Release: When tweetPtr goes out of scope, which would be at the end of the main function in this case, its destructor is automatically invoked. This destructor frees the associated heap memory, destroying the Tweet object. This automatic deallocation is the cornerstone of RAII—resources are cleaned up without explicit instructions from the developer.

By adhering to RAII principles through unique_ptr, C++ developers can write more robust applications. Smart pointers automate memory management, which not only simplifies code but also dramatically reduces the risk of resource leaks and errors. With unique_ptr, you have a powerful tool that aligns with modern C++ best practices, ensuring that resources are managed safely and efficiently.

Summary

By using unique_ptr, you can write safer programs with automatic memory management, reducing the risk of memory leaks and pointer errors.

2 thoughts on “What is unique_ptr in C++?”

Leave a Comment

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to Top