Skip to content

62 Intermediate The Hundred

Two Owners, One Corpse

two owners who never met

#include <iostream>
#include <memory>

int main() {
    int* raw = new int(42);

    std::shared_ptr<int> a(raw);   // a manages the int
    std::shared_ptr<int> b(raw);   // b helps out... right?

    std::cout << "a sees " << *a << " (use_count " << a.use_count() << ")" << std::endl;
    std::cout << "b sees " << *b << " (use_count " << b.use_count() << ")" << std::endl;
    std::cout << "leaving main..." << std::endl;
}

Run it. What does it print?

Answer

Both counts are 1, not 2 — and then it crashes. Typical output (GCC 11 / glibc on x86-64 Linux):

a sees 42 (use_count 1)
b sees 42 (use_count 1)
leaving main...
free(): double free detected in tcache 2
Aborted (core dumped)

Why

A shared_ptr constructed from a raw pointer doesn't join existing ownership — it founds it, allocating a brand-new control block with the count at 1. So a and b have never heard of each other; the use_count 1 printed twice is the tell. When the scope ends, each conscientiously deletes "its" int, and the second delete is undefined behavior — here glibc catches it and aborts, but it could just as well corrupt the heap silently or even appear to work. -Wall -Wextra says nothing; the code is perfectly legal to compile.

The fix

Establish ownership once, then copy the smart pointer — copies share one control block:

auto a = std::make_shared<int>(42);
auto b = a;   // use_count is now 2 — one owner group, one delete

The same trap fires when a member function wraps this in a fresh shared_ptr; the cure there is std::enable_shared_from_this.

Takeaway: ownership is established exactly once — after that, copy the smart pointer, never the raw one.

Try it: g++ -std=c++17 main.cpp -o demo && ./demo

Open in Compiler Explorer ↗ Quiz this entry