Skip to content
Theory · Memory and Smart Pointers · lesson 8 of 12

std::shared_ptr and weak_ptr

An object owned by a std::unique_ptr always has one owner. But sometimes several parts of a program use an object, and nobody knows in advance which one finishes last. That is what std::shared_ptr is for: it can be copied, and all the copies own one object.

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

int main() {
    std::shared_ptr<int> a = std::make_shared<int>(7);
    std::shared_ptr<int> b = a;
    *b = 8;
    std::cout << *a << ' ' << a.use_count() << '\n';  // 8 2
    b = nullptr;
    std::cout << a.use_count() << '\n';  // 1
}

std::make_shared<int>(7) creates the number on the heap together with its first owner. The line std::shared_ptr<int> b = a; copies ownership, not the number. a and b point to the same number, so after *b = 8, the value of *a is 8 too.

Counting owners

When an object belongs to all copies of a shared_ptr at once, that is shared ownership. In the left-luggage room, this is a locker with several tickets issued for it: the item is handed out only when the last of them comes back.

A shared_ptr counts the owners, and use_count() returns their number. Each copy adds one to the count. When an owner is destroyed or assigned nullptr, the count goes down.

Step through the program and watch how each copy changes the count and at which moment the Song is deleted.

Liveshared_ptr copies and the owner countstep 1 of 10 · frames: 1

make_shared creates a Song on the heap and its first owner, a

The number in brackets is the owner count, shared by all copies

The object is deleted when the count reaches zero, that is, together with its last owner. Counting costs time and memory, so shared_ptr is used only when there are several owners. Otherwise, std::unique_ptr is enough.

Weak pointer

std::weak_ptr is a weak pointer: it watches an object owned by shared_ptrs but does not own it. It is created from a shared_ptr, and use_count() does not grow because of it. The lock() method returns a shared_ptr to the object, or an empty shared_ptr if the object is already deleted. It is like a note with the locker number: it lets you check whether the item is still there, but it does not hold the locker.

C++
std::shared_ptr<int> owner = std::make_shared<int>(5);
std::weak_ptr<int> watcher = owner;
std::cout << owner.use_count() << '\n';  // 1
owner = nullptr;
std::cout << (watcher.lock() == nullptr) << '\n';  // 1

Common mistake

Two objects own each other through shared_ptr:

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

struct Node {
    std::shared_ptr<Node> next;
    ~Node() { std::cout << "~Node\n"; }
};

int main() {
    std::shared_ptr<Node> a = std::make_shared<Node>();
    std::shared_ptr<Node> b = std::make_shared<Node>();
    a->next = b;
    b->next = a;  // ✗
}

The -> operator works with smart pointers too.

The program prints nothing. At the end of main, a and b are destroyed, but each Node still owns its neighbor. Both counts stay at one, so the destructors never run and both objects leak.

A weak pointer breaks such a cycle. Declared as std::weak_ptr<Node> next;, the field does not own its neighbor, both counts reach zero, and the program prints ~Node twice. Real programs usually make one link of the two weak—for example, the one from a child object to its parent.

Key points

  • Copies of a std::shared_ptr own one object together, and use_count() returns the number of owners.
  • The object is deleted with its last owner, when the count reaches zero.
  • A std::weak_ptr watches an object without owning it: lock() returns an empty shared_ptr if the object is deleted.
  • Two objects that own each other through shared_ptr leak; one of the links is made a weak_ptr.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way