Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

  1. Home
  2. C++ Dictionary
  3. std::weak_ptr

std::weak_ptr

Introduced in C++11, std::weak_ptr is a smart pointer that holds a non-owning (weak) reference to an object managed by std::shared_ptr. Because it does not increment the reference count, it is used to break "circular references" — situations where two shared_ptr instances own each other, preventing the count from ever reaching zero and causing a memory leak. Replacing one side of the cycle with a weak_ptr solves the problem.

Syntax

// ========================================
// weak_ptr basics
// ========================================

#include <memory>

// Create from a shared_ptr (does not increase the reference count)
std::shared_ptr<int> sp = std::make_shared<int>(42);
std::weak_ptr<int>   wp = sp;

// Check validity
if (!wp.expired()) {
    // Promote to shared_ptr with lock() before accessing
    std::shared_ptr<int> locked = wp.lock();
    if (locked) {
        // safe to use the object through locked
    }
}

// When sp is released, wp becomes expired
sp.reset();
bool invalid = wp.expired();  // true (object has been freed)

Syntax Reference

Operation / MemberDescription
std::weak_ptr<T> wp = spCreates a weak_ptr from a shared_ptr. The reference count is not incremented.
wp.lock()Returns a shared_ptr if the observed object is still alive; returns an empty shared_ptr if it has already been freed.
wp.expired()Returns true if the observed object has already been freed.
wp.use_count()Returns the number of shared_ptr instances currently owning the observed object.
wp.reset()Empties the weak_ptr.
Breaking circular referencesIn an "A owns B, B owns A" relationship, changing one side to weak_ptr prevents the cycle.

Sample Code

eva_weak_ptr.cpp
// ========================================
// eva_weak_ptr.cpp
// Using the relationship between Eva pilots and NERV
// to demonstrate shared_ptr circular references and
// their resolution with weak_ptr
// ========================================

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

// ========================================
// Forward declaration
// Required because Pilot and Nerv reference each other
// ========================================
class Nerv;

// ========================================
// Pilot class
// A pilot holds a reference to the NERV organization.
// Using weak_ptr avoids a circular reference.
// ========================================
class Pilot {
public:
    std::string name;

    // Reference to Nerv is weak_ptr (prevents circular reference)
    std::weak_ptr<Nerv> organization;

    Pilot(const std::string& name) : name(name) {
        std::cout << "[create] Pilot: " << name << std::endl;
    }

    ~Pilot() {
        std::cout << "[free] Pilot: " << name << std::endl;
    }

    void report() {
        // Promote to shared_ptr with lock() before accessing
        std::shared_ptr<Nerv> org = organization.lock();
        if (org) {
            std::cout << name << " belongs to " << org->codeName << "." << std::endl;
        } else {
            std::cout << name << ": the organization no longer exists." << std::endl;
        }
    }
};

// ========================================
// Nerv class
// NERV holds strong ownership of its pilot.
// ========================================
class Nerv {
public:
    std::string codeName;

    // NERV owns the pilot, so shared_ptr is used
    std::shared_ptr<Pilot> assignedPilot;

    Nerv(const std::string& codeName) : codeName(codeName) {
        std::cout << "[create] Nerv: " << codeName << std::endl;
    }

    ~Nerv() {
        std::cout << "[free] Nerv: " << codeName << std::endl;
    }
};

// ========================================
// Function to demonstrate weak_ptr expiry check
// ========================================
void checkWeakPtr(const std::weak_ptr<Pilot>& wp) {
    if (wp.expired()) {
        std::cout << "  -> weak_ptr is expired (object has been freed)." << std::endl;
    } else {
        std::shared_ptr<Pilot> locked = wp.lock();
        std::cout << "  -> weak_ptr is valid. Pilot: " << locked->name
                  << "  ref count: " << locked.use_count() << std::endl;
    }
}

int main() {
    // ========================================
    // Part 1: Resolving circular reference with weak_ptr
    // ----------------------------------------
    // If Pilot::organization were shared_ptr<Nerv>:
    //   nerv  -> pilot (shared_ptr)  count: 1
    //   pilot -> nerv  (shared_ptr)  count: 1
    // Neither count reaches 0 when the scope ends,
    // so destructors are never called — memory leaks.
    // ========================================

    std::cout << "=== Part 1: Resolving circular reference with weak_ptr ===" << std::endl << std::endl;

    {
        std::shared_ptr<Nerv>  nerv  = std::make_shared<Nerv>("NERV HQ");
        std::shared_ptr<Pilot> pilot = std::make_shared<Pilot>("Ikari Shinji");

        // Set up the mutual references
        nerv->assignedPilot = pilot;   // Nerv owns Pilot (shared_ptr)
        pilot->organization = nerv;    // Pilot weakly references Nerv (weak_ptr)

        std::cout << "nerv  ref count: " << nerv.use_count()  << std::endl;
        std::cout << "pilot ref count: " << pilot.use_count() << std::endl;
        // nerv:  1 (weak_ptr does not increment)
        // pilot: 2 (nerv->assignedPilot + pilot itself)

        std::cout << std::endl;
        pilot->report();  // Access NERV info safely via weak_ptr.lock()

        std::cout << std::endl;
        std::cout << "--- Scope ends. Destructors are called in the correct order. ---" << std::endl;
    }
    // nerv freed -> assignedPilot (pilot) ref count drops to 1
    // pilot freed -> destructor called
    // Both destructors are called, confirming no memory leak

    std::cout << std::endl;
    std::cout << "=== Part 2: Checking weak_ptr expiry ===" << std::endl << std::endl;

    std::weak_ptr<Pilot> weakAyanami;

    {
        std::shared_ptr<Pilot> ayanami = std::make_shared<Pilot>("Ayanami Rei");
        weakAyanami = ayanami;

        std::cout << "While shared_ptr is alive:" << std::endl;
        checkWeakPtr(weakAyanami);

        std::cout << std::endl;
        std::cout << "--- Scope ends ---" << std::endl;
    }
    // ayanami shared_ptr goes out of scope and is freed

    std::cout << std::endl;
    std::cout << "After shared_ptr is freed:" << std::endl;
    checkWeakPtr(weakAyanami);  // expired() == true

    std::cout << std::endl;
    std::cout << "=== Part 3: Multiple pilots and use_count ===" << std::endl << std::endl;

    std::shared_ptr<Pilot> asuka  = std::make_shared<Pilot>("Soryu Asuka");
    std::shared_ptr<Pilot> kaworu = std::make_shared<Pilot>("Nagisa Kaworu");
    std::shared_ptr<Pilot> misato = std::make_shared<Pilot>("Katsuragi Misato");

    std::weak_ptr<Pilot> wAsuka  = asuka;
    std::weak_ptr<Pilot> wKaworu = kaworu;

    std::cout << std::endl;
    std::cout << "asuka  use_count: " << wAsuka.use_count()  << " (1 shared_ptr owns it)" << std::endl;
    std::cout << "kaworu use_count: " << wKaworu.use_count() << " (1 shared_ptr owns it)" << std::endl;

    // Release kaworu
    kaworu.reset();
    std::cout << std::endl;
    std::cout << "After kaworu.reset():" << std::endl;
    checkWeakPtr(wKaworu);  // expired() == true

    std::cout << std::endl;
    std::cout << "=== Done ===" << std::endl;

    return 0;
}
# Compile
g++ -std=c++11 eva_weak_ptr.cpp -o eva_weak_ptr

# Run
./eva_weak_ptr
=== Part 1: Resolving circular reference with weak_ptr ===

[create] Nerv: NERV HQ
[create] Pilot: Ikari Shinji
nerv  ref count: 1
pilot ref count: 2

Ikari Shinji belongs to NERV HQ.

--- Scope ends. Destructors are called in the correct order. ---
[free] Nerv: NERV HQ
[free] Pilot: Ikari Shinji

=== Part 2: Checking weak_ptr expiry ===

[create] Pilot: Ayanami Rei
While shared_ptr is alive:
  -> weak_ptr is valid. Pilot: Ayanami Rei  ref count: 2

--- Scope ends ---
[free] Pilot: Ayanami Rei

After shared_ptr is freed:
  -> weak_ptr is expired (object has been freed).

=== Part 3: Multiple pilots and use_count ===

[create] Pilot: Soryu Asuka
[create] Pilot: Nagisa Kaworu
[create] Pilot: Katsuragi Misato

asuka  use_count: 1 (1 shared_ptr owns it)
kaworu use_count: 1 (1 shared_ptr owns it)

After kaworu.reset():
[free] Pilot: Nagisa Kaworu
  -> weak_ptr is expired (object has been freed).

=== Done ===
[free] Pilot: Katsuragi Misato
[free] Pilot: Soryu Asuka

Common Mistakes

Here are two common pitfalls when using weak_ptr.

Forgetting to check the return value of lock()

lock() returns an empty shared_ptr (equivalent to nullptr) if the target object has already been freed. Accessing members without checking will cause a crash.

lock_check_mistake.cpp (NG)
// NG: using lock() return value without checking
std::weak_ptr<std::string> wp;
// ... after wp is set and sp has been freed ...
std::shared_ptr<std::string> locked = wp.lock();
std::cout << *locked << std::endl;  // crash (locked is nullptr)
lock_check_mistake.cpp (OK)
#include <iostream>
#include <memory>
#include <string>

int main() {
    std::weak_ptr<std::string> wp;

    {
        auto sp = std::make_shared<std::string>("Ikari Shinji");
        wp = sp;
    }
    // sp has gone out of scope and been freed here

    // OK: always check the return value of lock() with if
    std::shared_ptr<std::string> locked = wp.lock();
    if (locked) {
        std::cout << "Pilot: " << *locked << std::endl;
    } else {
        std::cout << "The object has already been freed." << std::endl;
    }

    return 0;
}
# Compile
g++ -std=c++11 lock_check_mistake.cpp -o lock_check_mistake

# Run
./lock_check_mistake
The object has already been freed.

Overlooking the circular reference pattern

When A holds B via shared_ptr and B also holds A via shared_ptr, neither reference count ever reaches zero and destructors are never called. Changing one side to weak_ptr breaks the cycle.

circular_ref_bad.cpp (NG — circular reference: destructors not called)
#include <iostream>
#include <memory>
#include <string>

struct NervBad;

struct PilotBad {
    std::string name;
    std::shared_ptr<NervBad> org;  // NG: shared_ptr causes a circular reference

    PilotBad(const std::string& n) : name(n) {
        std::cout << "[create] PilotBad: " << name << std::endl;
    }
    ~PilotBad() { std::cout << "[free] PilotBad: " << name << std::endl; }
};

struct NervBad {
    std::string code;
    std::shared_ptr<PilotBad> pilot;

    NervBad(const std::string& c) : code(c) {
        std::cout << "[create] NervBad: " << code << std::endl;
    }
    ~NervBad() { std::cout << "[free] NervBad: " << code << std::endl; }
};

int main() {
    auto nerv  = std::make_shared<NervBad>("NERV HQ");
    auto pilot = std::make_shared<PilotBad>("Ayanami Rei");
    nerv->pilot  = pilot;
    pilot->org   = nerv;
    // Scope ends but ref count stays at 1 — destructors never called (memory leak)
    return 0;
}
circular_ref_good.cpp (OK — weak_ptr: destructors called correctly)
#include <iostream>
#include <memory>
#include <string>

struct NervGood;

struct PilotGood {
    std::string name;
    std::weak_ptr<NervGood> org;  // OK: weak_ptr breaks the cycle

    PilotGood(const std::string& n) : name(n) {
        std::cout << "[create] PilotGood: " << name << std::endl;
    }
    ~PilotGood() { std::cout << "[free] PilotGood: " << name << std::endl; }
};

struct NervGood {
    std::string code;
    std::shared_ptr<PilotGood> pilot;

    NervGood(const std::string& c) : code(c) {
        std::cout << "[create] NervGood: " << code << std::endl;
    }
    ~NervGood() { std::cout << "[free] NervGood: " << code << std::endl; }
};

int main() {
    auto nerv  = std::make_shared<NervGood>("NERV HQ");
    auto pilot = std::make_shared<PilotGood>("Ikari Shinji");
    nerv->pilot  = pilot;
    pilot->org   = nerv;
    // weak_ptr does not increment ref count, so both are freed correctly
    return 0;
}
# Compile
g++ -std=c++11 circular_ref_good.cpp -o circular_ref_good

# Run
./circular_ref_good
[create] NervGood: NERV HQ
[create] PilotGood: Ikari Shinji
[free] NervGood: NERV HQ
[free] PilotGood: Ikari Shinji

Overview

std::weak_ptr is a non-owning weak reference used to break circular reference problems with std::shared_ptr. Unlike shared_ptr, it does not increment the reference count and therefore does not affect the object's lifetime. To access the object, call lock() to promote it to a shared_ptr. If the original shared_ptr has already been freed, lock() returns an empty shared_ptr, so always check with expired() or if (locked) before use. Typical use cases include parent-child mutual references (parent holds child via shared_ptr; child holds parent via weak_ptr), caches, and observer patterns where you want to observe without owning. Using weak_ptr correctly further improves the safety of resource management with shared_ptr. See also shared_ptr and unique_ptr.

If you find any errors or copyright issues, please .