Intermediate 13 min

Smart Pointers - unique_ptr

Master unique_ptr for automatic memory management with exclusive ownership

Learn how to use unique_ptr for automatic memory management with exclusive ownership, eliminating manual new/delete and preventing memory leaks.

A Simple Example

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

class Resource {
    std::string name;

public:
    explicit Resource(const std::string& n) : name{n} {
        std::cout << "Resource " << name << " created\n";
    }

    ~Resource() {
        std::cout << "Resource " << name << " destroyed\n";
    }

    void use() const {
        std::cout << "Using resource: " << name << "\n";
    }
};

void demonstrateUniquePtr() {
    // Create unique_ptr - modern way
    auto ptr1{std::make_unique<Resource>("R1")};
    ptr1->use();

    // Automatic cleanup when going out of scope
    {
        auto ptr2{std::make_unique<Resource>("R2")};
        ptr2->use();
    }  // ptr2 destroyed, R2 automatically deleted

    std::cout << "After inner scope\n";

}  // ptr1 destroyed, R1 automatically deleted

int main() {
    demonstrateUniquePtr();
    std::cout << "All resources cleaned up automatically!\n";
    return 0;
}

Breaking It Down

std::make_unique<T>(...)

  • What it does: Creates a unique_ptr and the object it manages in one safe step
  • Why use it: Safer than unique_ptr<T>(new T) - prevents memory leaks in exception scenarios
  • Syntax: auto ptr{std::make_unique<Type>(constructor_args)};
  • Remember: This is the modern C++ way to create unique_ptr - avoid using new directly

Automatic Memory Management (RAII)

  • What it does: Automatically deletes the owned object when unique_ptr goes out of scope
  • Why it matters: No more forgetting to call delete - prevents memory leaks
  • How it works: The destructor of unique_ptr calls delete on the managed object
  • Remember: You never need to manually delete objects owned by unique_ptr

Exclusive Ownership

  • What it means: Only one unique_ptr can own an object at a time
  • Copying is disabled: You cannot copy a unique_ptr - only move it
  • Moving transfers ownership: Use std::move() to transfer ownership to another unique_ptr
  • Remember: The name "unique" means unique ownership - exactly one owner

Zero Runtime Overhead

  • Performance: unique_ptr compiles to the exact same machine code as raw pointers
  • Size: A unique_ptr is the same size as a raw pointer (typically 8 bytes)
  • Optimization: The compiler optimizes away all the safety features at compile time
  • Remember: You get automatic memory management for free - no performance cost

Why This Matters

  • Manual memory management with new/delete causes memory leaks, double-frees, and dangling pointers.
  • unique_ptr automates this entirely using RAII - memory is freed automatically when the pointer goes out of scope.
  • Modern C++ code should rarely use raw new/delete.
  • unique_ptr gives you the safety of automatic memory management with zero runtime overhead compared to raw pointers.

Critical Insight

unique_ptr has ZERO runtime overhead compared to raw pointers. The compiler optimizes it to the exact same machine code. You get automatic memory management for free!

The restriction that you cannot copy unique_ptr is actually a feature - it prevents accidental double-deletion bugs at compile time. If you need to share ownership, that is what shared_ptr is for (coming soon).

Best Practices

Always use std::make_unique: Prefer auto ptr{std::make_unique<T>(...)} over unique_ptr<T>(new T(...)) - it is safer and exception-safe.

Use unique_ptr by default: When you need heap allocation with ownership, unique_ptr should be your default choice.

Move instead of copy: Transfer ownership using std::move() when you need to pass unique_ptr to another owner.

Return unique_ptr from functions: Returning unique_ptr by value efficiently transfers ownership to the caller.

Common Mistakes

Using new directly: Do not write unique_ptr<T>(new T) - use std::make_unique instead. It is safer and prevents leaks in exception scenarios.

Trying to copy: unique_ptr cannot be copied. Use std::move() to transfer ownership, or use shared_ptr if you need sharing.

Storing unique_ptr unnecessarily: If you do not own the object, use a raw pointer or reference, not unique_ptr. Ownership should be clear.

Forgetting #include <memory>: You must include the <memory> header to use std::unique_ptr and std::make_unique.

Debug Challenge

This code has a memory management issue. Click the highlighted line to fix it:

1 #include <memory>
2 #include <iostream>
3
4 int main() {
5 auto ptr{new int{42}};
6 std::cout << *ptr << "\n";
7 return 0;
8 }

Quick Quiz

  1. What happens when unique_ptr goes out of scope?
The owned object is automatically deleted
Nothing, memory leaks
Undefined behavior
  1. Can you copy a unique_ptr?
Yes, freely
No, only move
Yes, but only with std::copy()
  1. What is the overhead of unique_ptr vs raw pointer?
Significant - avoid in performance code
Minimal - only destructor cost
Zero - compiles to same code

Practice Playground

Time to try out what you just learned! Play with the example code below, experiment by making changes and running the code to deepen your understanding.

Lesson Progress

  • Fix This Code
  • Quick Quiz
  • Practice Playground - run once