C++ smart pointers and ownership

Use unique_ptr for single ownership, shared_ptr for shared ownership, and weak_ptr to observe.

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

struct Resource {
  std::string name;
  explicit Resource(std::string n) : name(std::move(n)) { std::cout << "create " << name << "\n"; }
  ~Resource() { std::cout << "destroy " << name << "\n"; }
};

int main() {
  auto a = std::make_unique<Resource>("A");
  auto b = std::move(a);
  std::cout << "a is null: " << (a == nullptr) << "\n";

  {
    auto shared = std::make_shared<Resource>("S");
    auto second = shared;
    std::cout << "owners: " << shared.use_count() << "\n";
  }

  std::weak_ptr<Resource> weak;
  {
    auto temp = std::make_shared<Resource>("T");
    weak = temp;
    std::cout << "alive inside: " << !weak.expired() << "\n";
  }
  std::cout << "alive after: " << !weak.expired() << "\n";
  std::cout << "end of main\n";
}
Output
create A
a is null: 1
create S
owners: 2
destroy S
create T
alive inside: 1
destroy T
alive after: 0
end of main
destroy A

Explanation

Smart pointers manage heap objects automatically, following RAII: the object is destroyed when its owner goes away. std::unique_ptr has exactly one owner, cannot be copied, and can be moved to transfer ownership. After a move the source is null. It costs nothing extra compared with a raw pointer.

std::shared_ptr allows several owners and keeps a reference count. The object is destroyed when the last owner is gone. std::weak_ptr observes a shared object without owning it, so it does not keep it alive, and expired tells whether it still exists. Prefer unique_ptr by default and make_unique and make_shared over new.

Explore the dry run
Guided walkthroughReady
1#include <iostream>
2#include <memory>
3#include <string>
4
5struct Resource {
6 std::string name;
7 explicit Resource(std::string n) : name(std::move(n)) { std::cout << "create " << name << "\n"; }
8 ~Resource() { std::cout << "destroy " << name << "\n"; }
9};
10
11int main() {
12 auto a = std::make_unique<Resource>("A");
13 auto b = std::move(a);
14 std::cout << "a is null: " << (a == nullptr) << "\n";
15
16 {
17 auto shared = std::make_shared<Resource>("S");
18 auto second = shared;
19 std::cout << "owners: " << shared.use_count() << "\n";
20 }
21
22 std::weak_ptr<Resource> weak;
23 {
24 auto temp = std::make_shared<Resource>("T");
25 weak = temp;
26 std::cout << "alive inside: " << !weak.expired() << "\n";
27 }
28 std::cout << "alive after: " << !weak.expired() << "\n";
29 std::cout << "end of main\n";
30}

Follow the example

Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.

View the complete dry-run table
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput so far
make_unique creates the object and a unique_ptr that owns it.—
create A
Ownership moves from a to b. unique_ptr cannot be copied, only moved.—
create A
After the move a no longer owns anything.—
create A
a is null: 1
Copying a shared_ptr adds an owner.reference count = 2
create A
a is null: 1
Two shared_ptrs own S. When both leave the block, S is destroyed.reference count = 2
create A
a is null: 1
create S
owners: 2
destroy S
weak observes T without owning it.reference count = 2
create A
a is null: 1
create S
owners: 2
destroy S
While temp exists, the object is alive.reference count = 2
create A
a is null: 1
create S
owners: 2
destroy S
create T
alive inside: 1
destroy T
T was destroyed when temp left its block, so the weak_ptr is expired.reference count = 2
create A
a is null: 1
create S
owners: 2
destroy S
create T
alive inside: 1
destroy T
alive after: 0
b still owns A. It is destroyed when main returns, after this line.reference count = 2
create A
a is null: 1
create S
owners: 2
destroy S
create T
alive inside: 1
destroy T
alive after: 0
end of main
destroy A

Cost and common mistakes

unique_ptr costs nothing extra; shared_ptr updates a reference count (atomic) on copies.

  • Two objects holding shared_ptrs to each other form a cycle and are never destroyed. Break the cycle with a weak_ptr.
  • Never create two unrelated smart pointers from the same raw pointer. Both would try to delete the object.
  • Pass a smart pointer by value only when transferring or sharing ownership. Otherwise pass a reference or a raw pointer to the object.

Try it yourself

What would happen if you tried to copy a unique_ptr?

Reveal answer and reasoning

The program would not compile, because unique_ptr's copy constructor is deleted. You must use std::move.

Further reading