Skip to content
  • shared_ptr & weak_ptr · C++

shared_ptr & weak_ptr

LearnViaNotes @learnvianotes· 3 min read

std::shared_ptr allows shared ownership through reference counting; the object is destroyed when the last owner lets go. std::weak_ptr observes without owning — the cure for reference cycles.

Reference counting

C++
#include <iostream>
#include <memory>
#include <string>

struct Texture {
    std::string file;
    explicit Texture(std::string f) : file(std::move(f)) {}
    ~Texture() { std::cout << "unload " << file << '\n'; }
};

int main() {
    auto tex = std::make_shared<Texture>("grass.png");   // count = 1
    std::cout << tex.use_count() << '\n';
    {
        auto sprite1 = tex;                               // count = 2
        std::shared_ptr<Texture> sprite2 = tex;           // count = 3
        std::cout << tex.use_count() << '\n';
    }                                                     // back to 1
    std::cout << tex.use_count() << '\n';
    tex.reset();                                          // 0 → destroyed
    std::cout << "done\n";
}
Output
1
3
1
unload grass.png
done

How it works

A shared_ptr is two pointers: one to the object, one to a control block holding the strong count, the weak count and the deleter. make_shared allocates object and control block in one allocation.

unique_ptrshared_ptr
OwnersExactly oneMany
CopyableNo (move only)Yes (increments the count)
Size1 pointer2 pointers
CostFreeAtomic increments/decrements on copy/destroy
Default choice?YesOnly when ownership is genuinely shared

Cycles leak — weak_ptr breaks them

C++
#include <iostream>
#include <memory>
#include <string>

struct Employee;
struct Team {
    std::string name;
    std::shared_ptr<Employee> lead;         // owns its lead
    ~Team() { std::cout << "~Team\n"; }
};
struct Employee {
    std::string name;
    std::weak_ptr<Team> team;               // observes: does NOT keep the team alive
    ~Employee() { std::cout << "~Employee\n"; }
};

int main() {
    auto team = std::make_shared<Team>();
    auto ada = std::make_shared<Employee>();
    team->name = "core";
    team->lead = ada;
    ada->team = team;
    if (auto t = ada->team.lock())          // lock(): shared_ptr if still alive, else empty
        std::cout << "ada is in " << t->name << '\n';
    std::cout << team.use_count() << ' ' << ada.use_count() << '\n';
}   // both destroyed; with shared_ptr in both directions neither destructor would run
Output
ada is in core
1 2
~Team
~Employee
C++
#include <iostream>
#include <memory>
int main() {
    std::weak_ptr<int> watcher;
    {
        auto p = std::make_shared<int>(42);
        watcher = p;
        std::cout << watcher.expired() << ' ' << *watcher.lock() << '\n';
    }
    std::cout << watcher.expired() << ' ' << (watcher.lock() == nullptr) << '\n';
}
Output
0 42
1 1

enable_shared_from_this

C++
#include <iostream>
#include <memory>

struct Session : std::enable_shared_from_this<Session> {
    std::shared_ptr<Session> self() { return shared_from_this(); }   // NOT shared_ptr<Session>(this)!
};

int main() {
    auto s = std::make_shared<Session>();
    auto again = s->self();
    std::cout << s.use_count() << '\n';     // shares the same control block
}
Output
2

Is shared_ptr thread-safe?

When would you use weak_ptr?

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report