thecodex.expert · The Codex Family of Knowledge
C++

Smart Pointers

A unique_ptr genuinely cannot be copied — only moved — which is precisely what guarantees there’s only ever one owner, and therefore only ever one delete, with zero chance of a double-free.

C++20 / C++23 unique_ptr by default, shared_ptr when needed Last verified:
Canonical Definition

std::unique_ptr wraps sole ownership of a heap object — it cannot be copied, only moved, which structurally guarantees exactly one owner exists and therefore exactly one delete ever happens. std::shared_ptr instead uses atomic reference counting, allowing multiple owners; the underlying object is deleted only when the last shared_ptr referencing it is destroyed. std::make_unique and std::make_shared are the preferred way to create either, avoiding a naked new entirely. Modern C++ guidance is direct: default to unique_ptr, and reach for shared_ptr only when genuine shared ownership is actually needed.

unique_ptr: sole ownership, zero overhead

The object is automatically deleted when p goes out of scope — no explicit delete anywhere, and the copy line below genuinely won't compile, which is exactly the guarantee that prevents a double-free.

C++unique_ptr_basics.cpp
struct Person {
    std::string name;
    Person(std::string n) : name(std::move(n)) {}
    void greet() { std::cout << "Hi, I'm " << name << "\n"; }
};

std::unique_ptr<Person> p = std::make_unique<Person>("Alice");
p->greet();
// no delete needed — happens automatically when p goes out of scope

// std::unique_ptr<Person> p2 = p;   // COMPILE ERROR: unique_ptr cannot be copied
std::unique_ptr<Person> p2 = std::move(p);   // MOVING is fine — ownership transfers, p is now empty

shared_ptr: reference-counted shared ownership

The object is deleted only when the LAST shared_ptr referencing it is destroyed — use_count() shows exactly how many owners currently exist.

C++shared_ptr_basics.cpp
struct Person {
    std::string name;
    Person(std::string n) : name(std::move(n)) {}
};

std::shared_ptr<Person> p1 = std::make_shared<Person>("Bob");
std::cout << p1.use_count() << "\n";   // 1

std::shared_ptr<Person> p2 = p1;   // COPYING is fine — both now share ownership
std::cout << p1.use_count() << "\n";   // 2

p1.reset();   // p1 gives up its ownership
std::cout << p2.use_count() << "\n";   // 1 — object still alive, p2 still owns it
// object is deleted only when p2 ALSO goes out of scope or is reset

Why raw new/delete is discouraged in modern C++

make_unique and make_shared avoid an explicit new entirely, and the resulting object's lifetime is tied structurally to the smart pointer's scope — there's no code path (including an early return or an exception) that can accidentally skip the cleanup, which was always the real risk with manual new/delete pairing.

Sources

1
ISO/IEC 14882 (C++ Standard), "Smart pointers," cppreference.com/w/cpp/memory, covering unique_ptr and shared_ptr.