SyntaxStudy
Sign Up
C++ weak_ptr: Breaking Cycles and Observing Ownership
C++ Beginner 1 min read

weak_ptr: Breaking Cycles and Observing Ownership

'std::weak_ptr' is a non-owning smart pointer that holds a reference to an object managed by a 'shared_ptr' without affecting the reference count. It is the standard solution for breaking ownership cycles in object graphs — for instance, when a parent holds 'shared_ptr' to its children and children need a reference back to their parent. Making the back-reference a 'weak_ptr' prevents the cycle from keeping both alive indefinitely. Before using the object through a 'weak_ptr', the caller must check whether the object still exists by calling 'lock()', which returns a 'shared_ptr'. If the managed object has already been destroyed, 'lock()' returns an empty 'shared_ptr'. This two-step access is necessary because the object could be destroyed between an existence check and the actual use, particularly in multithreaded code. 'std::enable_shared_from_this' is a base class that allows an object managed by 'shared_ptr' to safely create additional 'shared_ptr' instances pointing to itself from within member functions. This is needed when a callback or asynchronous operation must hold a reference to the object for the duration of its execution, ensuring the object is not destroyed before the callback completes.
Example
#include <iostream>
#include <memory>
#include <string>
#include <vector>

struct Child;

struct Parent {
    std::string name;
    std::vector<std::shared_ptr<Child>> children;

    explicit Parent(std::string n) : name(std::move(n)) {
        std::cout << "Parent '" << name << "' created\n";
    }
    ~Parent() { std::cout << "Parent '" << name << "' destroyed\n"; }
};

struct Child {
    std::string name;
    std::weak_ptr<Parent> parent;   // weak — no ownership cycle

    explicit Child(std::string n) : name(std::move(n)) {
        std::cout << "Child '" << name << "' created\n";
    }
    ~Child() { std::cout << "Child '" << name << "' destroyed\n"; }

    void greetParent() const {
        if (auto p = parent.lock()) {   // lock() returns shared_ptr or empty
            std::cout << name << " greets parent: " << p->name << "\n";
        } else {
            std::cout << name << ": parent is gone\n";
        }
    }
};

// enable_shared_from_this example
class Timer : public std::enable_shared_from_this<Timer> {
public:
    void scheduleCallback() {
        // Safe: captures a shared_ptr to self
        auto self = shared_from_this();
        std::cout << "Timer scheduled with use_count="
                  << self.use_count() << "\n";
    }
};

int main() {
    auto parent = std::make_shared<Parent>("Alice");
    auto child  = std::make_shared<Child>("Bob");
    child->parent = parent;             // weak link — no cycle
    parent->children.push_back(child);

    child->greetParent();

    parent.reset();   // Parent destroyed (no cycle)
    child->greetParent();   // parent is gone

    // enable_shared_from_this
    auto timer = std::make_shared<Timer>();
    timer->scheduleCallback();

    return 0;
}

This is the last lesson in this section.

Create a free account to earn a certificate