cpp-que-son-smart-pointers

What Are Smart Pointers and How to Use Them in C++

  • 5 min

A Smart Pointer is an object that automatically manages a pointer and releases the resource when it is no longer needed.

If you’ve been programming in C++ for a while, you’ll know that one of the biggest headaches in the language is manual memory management.

In classic C++ (“C with classes”), we were responsible for allocating memory with new and freeing it with delete. If you forgot the delete, you had a memory leak. If you did it twice, memory corruption. If you accessed memory after deletion, segmentation fault. A minefield.

To solve this, C++11 introduced Smart Pointers.

A Smart Pointer is a class that wraps a “raw” pointer and automatically manages its lifecycle. Basically, they ensure that memory is released when it is no longer needed, without you having to remember to do it yourself.

Using Smart Pointers is the foundation of Modern C++. Today, using manual new and delete is considered bad practice in the vast majority of cases.

The Problem with Raw Pointers

To understand why we use smart pointers, let’s first recall the problem with this classic code:

void processData() {
    MyObject* obj = new MyObject(); // Allocate memory
    obj->doSomething();
    
    // ... complex code ...
    
    if (hasError) {
        return; // OOPS! We left without doing delete. Memory leak.
    }

    delete obj; // If we get here, all good. If not... bad news.
}
Copied!

If the function returns early (due to an if statement or an exception being thrown), the line delete obj never executes. That memory remains occupied forever (until you close the program).

Smart Pointers solve this by applying a fundamental C++ concept: RAII.

RAII: Binding Resources and Objects

The operation of Smart Pointers is based on the RAII idiom (Resource Acquisition Is Initialization).

The idea is simple: tie the lifecycle of a resource (memory, files, sockets) to the lifecycle of an object.

When we create a Smart Pointer (a variable on the Stack), it acquires the resource (the memory on the Heap). When the Smart Pointer goes out of scope, its destructor runs automatically and frees the memory.

Let’s see the same example but with a Smart Pointer:

#include <memory> // Required to use smart pointers

void processData() {
    std::unique_ptr<MyObject> obj = std::make_unique<MyObject>();
    
    obj->doSomething(); // Use it like a normal pointer (->)
    
    if (hasError) {
        return; // The destructor of 'obj' fires here. Memory freed.
    }

} // The destructor of 'obj' fires here too. Memory freed.
Copied!

No matter how we exit the function. Memory is always freed, which is exactly what we wanted.

Types of Smart Pointers

In the standard library <memory>, we have three main types of smart pointers, each designed for a different ownership strategy.

std::unique_ptr (Unique Owner)

It is the most common and efficient. It represents exclusive ownership.

  • Only one unique_ptr can own the object at a time.
  • It cannot be copied (it wouldn’t make sense to have two unique owners).
  • It can be “moved” (transfer ownership to another).
  • It has zero performance overhead (it is as fast as a raw pointer).

Use it by default. If you don’t know which one to use, use std::unique_ptr.

std::shared_ptr (Shared Ownership)

It represents shared ownership through a reference counter.

  • Several shared_ptr can point to the same object.
  • It keeps an internal count (reference count) of how many pointers are using it.
  • Memory is only freed when the last shared_ptr is destroyed (the counter reaches 0).

weak_ptr (Weak Observer)

It is a companion to shared_ptr.

  • It points to an object managed by a shared_ptr but does not increase the reference count.
  • It is used to break circular references and to observe if an object is still alive without owning it.

What Happened to auto_ptr?

If you come from very old versions of C++ (pre-C++11), you might remember std::auto_ptr.

std::auto_ptr is deprecated and was removed in C++17. It had serious design problems when copying, so do not use it in new code. Its direct replacement is std::unique_ptr.

Syntax Comparison

Although we will dive into each one in their own articles, here is a quick table comparing how they are declared and used versus raw pointers.

ActionRaw Pointer (Legacy)Smart Pointer (Modern)
HeaderNone#include <memory>
Creationnew Type()std::make_unique<Type>()
Accessptr->method()ptr->method() (Same)
Dereference*ptr*ptr (Same)
Releasedelete ptrAutomatic

Which one should I use?

A practical guideline in modern C++ is:

  1. Use local objects (on the stack) whenever possible.
  2. If you need dynamic memory, use std::unique_ptr.
  3. If you really need to share ownership among multiple owners (not just passing the pointer for them to use, but sharing the responsibility of deleting it), use std::shared_ptr.
  4. Avoid using new and delete explicitly, except in low-level code where you genuinely need to manage the resource manually.