No history yet

Introduction to Smart Pointers

Smarter Memory Management

As we've seen, working with raw pointers in C++ gives you a lot of power. You can directly manage memory with new and delete. But this power comes with responsibility. It's easy to make mistakes that lead to bugs that are hard to track down.

Two common problems are memory leaks, where you forget to call delete, and dangling pointers, where you use a pointer after its memory has already been deleted.

Consider a function that allocates some memory. If an error occurs and the function exits early, the delete statement at the end might never be reached. The allocated memory is now lost forever, becoming a leak.

#include <stdexcept>

void riskyFunction(bool should_throw) {
    int* raw_ptr = new int(42);

    if (should_throw) {
        throw std::runtime_error("An error occurred!");
    }

    // This line is skipped if an exception is thrown.
    delete raw_ptr;
}

This is where smart pointers come in. They are special objects that act like pointers but handle memory management automatically. They wrap a raw pointer and, when the smart pointer object itself is destroyed, it automatically calls delete on the raw pointer it holds. This principle is called RAII, or Resource Acquisition Is Initialization.

Smart pointers, such as std::unique_ptr and std::shared_ptr, take care of memory lifespan automatically.

By using smart pointers, you make your code safer and easier to read. You tell your fellow programmers—and the compiler—exactly what the ownership rules are for a piece of memory.

Three Kinds of Ownership

The C++ Standard Library provides three main types of smart pointers, each designed for a different kind of ownership.

unique_ptr

other

A smart pointer that exclusively owns an object. It cannot be copied, only moved. This is the most efficient smart pointer.

Think of std::unique_ptr as holding the one and only key to a resource. You can't make a copy of the key, but you can pass the original key to someone else. When the last person holding the key is done, the resource is automatically cleaned up. This makes it perfect for managing objects within a single scope or class.

shared_ptr

other

A smart pointer that allows multiple pointers to share ownership of the same object. It keeps a reference count of how many shared_ptrs point to the object.

std::shared_ptr works like a library book. Multiple people can check out a copy, and the library keeps track of how many copies are out. The book (the memory) is only removed from circulation (deleted) when the very last person returns their copy. This is useful when an object's lifetime isn't tied to a single owner.

weak_ptr

other

A smart pointer that holds a non-owning reference to an object managed by a shared_ptr. It doesn't affect the object's lifetime.

std::weak_ptr is a special companion to shared_ptr. It lets you observe an object without keeping it alive. If the library book from our last example is destroyed because everyone returned their copy, your weak_ptr lets you see that the book is no longer available. This is crucial for preventing a situation where two objects use shared_ptrs to own each other, creating a cycle where neither is ever deleted.

Smart PointerOwnership ModelCan Be Copied?Use Case
std::unique_ptrExclusive / UniqueNo (Can be moved)When one pointer should own the resource.
std::shared_ptrShared / CountedYesWhen multiple pointers need to share ownership.
std::weak_ptrNon-owning referenceYesTo observe a shared_ptr without ownership.

Moving forward, we'll dive into how to use each of these smart pointers in your code. The general rule is to start with std::unique_ptr because it's the simplest and fastest, and only use std::shared_ptr when you genuinely need shared ownership.