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
#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";
}1
3
1
unload grass.png
doneHow 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_ptr | shared_ptr | |
|---|---|---|
| Owners | Exactly one | Many |
| Copyable | No (move only) | Yes (increments the count) |
| Size | 1 pointer | 2 pointers |
| Cost | Free | Atomic increments/decrements on copy/destroy |
| Default choice? | Yes | Only when ownership is genuinely shared |
Cycles leak — weak_ptr breaks them
#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 runada is in core
1 2
~Team
~Employee#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';
}0 42
1 1enable_shared_from_this
#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
}2Is shared_ptr thread-safe?
When would you use weak_ptr?