UNIQUE_PTR in C++ : In-Dept analysis

std::unique_ptr in C++ is a smart pointer in that provides automatic memory management for dynamically allocated objects. It is part of the C++11 standard and helps manage the lifetime of dynamically allocated resources.

First let’s see the key properties and example of the Unique pointer in C++, that will give clear picture about it. The we will discuss on unique pointers and function.

Key properties of Unique_ptr:

  • Uniqueness: A unique_ptr “owns” the object it points to, ensuring that only one unique_ptr can manage a particular object. This enforces a strict one-to-one ownership relationship.
  • Copying and Ownership: unique_ptr cannot be directly copied, as it would violate the uniqueness principle. However, it can be moved, transferring ownership from one unique_ptr to another.
  • Lifetime Management: The managed object is automatically deleted when the unique_ptr goes out of scope, ensuring that memory is properly deallocated.
  • Use Cases: unique_ptr is ideal for cases where exclusive ownership is required, such as managing resources that must be released explicitly, like dynamically allocated memory or file handles.

unique_ptr  Example:

#include <iostream>
#include <bits/stdc++.h>
using namespace std;

int main() {
    std::unique_ptr<int> myPtr = std::make_unique<int>(35);
    // myPtr owns the dynamically allocated int with value 35

    std::unique_ptr<int> anotherPtr = std::move(myPtr);
    // ownership transferred, myPtr is now null, anotherPtr owns the object

    if (anotherPtr) {
        // Check if anotherPtr is not null before using it
        int value = *anotherPtr; // Access the value
        // no need to delete, memory will be automatically freed
    } // anotherPtr goes out of scope, the object is deleted

    return 0;
}

Highlights:

  • Advantages: Prevents memory leaks by automatically managing memory deallocation. Eliminates the need for manual delete, reducing the chance of errors. Helps enforce clear ownership semantics.
  • When to Use: Use unique_ptr when you need a single owner for a dynamically allocated resource. Avoid using it in scenarios where shared ownership is required.
  • When It Dies: The managed object is deleted automatically when the unique_ptr goes out of scope, ensuring timely resource release.

Passing unique_ptr in C++ to a function

Here’s 2 points to note:

  • You cannot pass a unique pointer to a function by value, but you can pass it by reference.
  • However, you can use move semantic to transfer ownership to the unique pointer declared in the function parameter, then it will work. Examples below will clarify you.

Passing by value:

You cannot pass the unique pointer ptr to the function f(std::unique_ptr<int> p) by value, because the unique pointer ptr in the main() holds the ownership of the int.  when you pass it as f(ptr), it will look like std::unique_ptr<int> p =  ptr, // COPY, so there is an ERROR on making copy because you cannot a copy of the unique pointer.

void f(std::unique_ptr<int> p) {
    cout<<*p;
}

int main() {
  auto ptr = std::make_unique<int>(100);
 f(ptr);
 
  return 0;
}

Passing by reference:

In the above code, If you pass the unique pointer by reference, then it’s ok. NOTICE the unique pointer declaration in the function f(std::unique_ptr<int>& p). The code will print the output 100 inside the function.

Using move semantic:

Here, the move function transferring the ownership to unique pointer “p”, declared in the function. So, this code will work fine.

void f(std::unique_ptr<int> p) {
    cout<<*p;
}

int main() {
  auto ptr = std::make_unique<int>(100);
  // use move to transfer ownership of the object from
  //ptr to the function pointer p.
  f(move(ptr));
 
  return 0;
}

Returning unique_ptr from a function:

You can return a unique pointer from a function. The below code works fine.

CATCH: We discussed that you cannot make a copy of an unique pointer. That’s right. But, here when you return the unique pointer and assign it to the q pointer in the main function, there should be an error, but it works. HOW? Answer is: copy elision. Returning a unique pointer allowed.

auto f(std::unique_ptr<int> p) {
  *p = 50;
  return p;
}

int main() {
  auto ptr = std::make_unique<int>(100);
  cout<< "Before function call..."<<*ptr <<endl;
  std::unique_ptr<int> q = f(move(ptr));
  cout<< "After function call..."<<*q;
  return 0;
}