“Know how to solve every problem that has been solved.” “What I cannot create, I do not understand.” — Richard Feynman

Ownership in C++, taught by the diff

Programming

What you need to know first 1 concepts, 1 layers

The requisite-knowledge inventory for this page, bottom-up: the primitives at the base, combined upward until you reach what this page assumes. Skim the layers you already own; start wherever the ground gets unfamiliar.

  1. base
  2. you are here

This is the first entry in a dictionary of programs: a concept taught not by a paragraph of definition but by the diff between programs that differ by one feature at a time. Like a dictionary defines a word using other words, each step here defines a C++ ownership idea in terms of the one before it — and the leaves are nailed to reality, because every program below compiles, runs, and prints exactly the output shown.

The chain runs from a stack variable (cleanup for free) to shared_ptr (cleanup by reference count), one feature per step. Two of the steps are true minimal pairs — change a single line, watch one concept appear: comment out a delete and the destructor vanishes from the output (that's a memory leak you can see); swap new for make_unique and the destructor comes back with no cleanup line anywhere (that's what a smart pointer is).

Step through with the arrows. Changed lines are marked and highlighted. Hover any line with a left-bar for its rationale — the panel shows what changed between programs by default, and the line's own "why" while you hover it.

1 / 6
Stack variable — the ground truth
starting point
#include <iostream>
 
struct Widget {
int v;
Widget(int v) : v(v) { std::cout << "ctor " << v << "\n"; }
~Widget() { std::cout << "dtor " << v << "\n"; }
};
 
int main() {
Widget w(7);
std::cout << "use " << w.v << "\n";
}
the starting point
An object with automatic lifetime. w lives on the stack; its destructor runs the instant main returns, with no instruction from you. Watch for dtor 7 in the output — that's cleanup happening for free. Everything that follows is about recovering this guarantee once the object has to outlive its scope.
hover a marked line (left bar) for its rationale
what it prints
ctor 7
use 7
dtor 7

The leak is the missing line

The grounding move is step 3. Every other program in the chain ends its output with dtor 7 — the destructor ran, cleanup happened. The forgot-the-delete variant ends at use 7, and the destructor message never appears. No sanitizer, no tooling: the leak is the absence of a line you can see in the other five outputs. That's the whole reason the rest of the chain exists. A unique_ptr is the smallest change that puts the dtor back automatically; shared_ptr is the same idea once a single owner isn't enough.

Stated as a citeable, falsifiable claim: a unique_ptr is a raw pointer that calls delete in its own destructor. The experiment is above — the dtor 7 line returns in step 4 with no manual cleanup. It would be falsified by a unique_ptr whose managed object leaks at scope exit; no such case exists for the default deleter.

Read next