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.
- base
- ↳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.
#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"; }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.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
- Raw pointers — the prerequisite this chain builds the first diff against.
- Struct and classes — where constructors and destructors come from.
- Claims — why the site is built as a graph of small, grounded, falsifiable facts, of which "a unique_ptr deletes itself" is one.