Intuition The ONE core idea
A raw pointer tells you where an object lives but nothing about who is responsible for destroying it . A smart pointer is a tiny object that answers that second question by tying the object's death to the moment the smart pointer itself goes out of scope — so the machine, not the forgetful human, runs the delete.
Before you can read a single line of the parent topic , you must own every symbol it silently assumes. This page builds them one at a time, from absolute zero. Nothing is used before it is earned.
Picture your computer's memory as a very long street of numbered houses. Each house holds one byte. Every house has a number — that number is called an address .
Definition Object and address
An object is a house (or run of houses) that stores a value — say the number 42.
An address is the house number where that value lives. Nothing more: just a number that says "look here."
Intuition Why we care about addresses at all
If a value lives inside one function but a different function needs to read or change the same value (not a copy of it), the only way to say "that exact house over there" is to hand over its house number. That house number is what pointers carry.
T*
A raw pointer is a variable whose value is an address . The T says what kind of thing lives at that address (int* = "address of an int"; Widget* = "address of a Widget").
Read the star * in a type as the words “pointer to” .
So int* p; reads left-to-right as "p is a pointer to an int" — p is an arrow that can point at a house holding an int.
Definition The two operators every pointer uses
&x — the address-of operator. Read & as “address of” . &x = "the house number where x lives."
*p — the dereference operator. Read *p as ==“the value at the house p points to.”== This is the reverse of &: & turns a value into an address; * turns an address back into the value.
Intuition Why two symbols and not one
They answer opposite questions. &x asks "where does this live?" (value → address). *p asks "what lives here?" (address → value). You need both because pointing at something and looking at the thing pointed-at are genuinely different actions.
* means two different things
In a type (int* p) the star means "pointer to". In an expression (*p) the star means "dereference — go look". Same symbol, two jobs. Which one it is depends on whether you are declaring or using.
Memory comes in two neighbourhoods. Most variables live on the stack : they are created when you enter a { } block and destroyed automatically when you leave it. But sometimes you need a house that outlives the block that made it. For that you ask the system for a house from the heap .
new and delete
new int(42) — ask the heap for a fresh house, put 42 in it, and hand back its address . The house stays until you return it.
delete p — return the house at address p to the system so it can be reused.
Intuition Why a leak happens
Every new must be matched by exactly one delete. A heap house is not cleaned up when you leave a block — only the stack pointer variable p disappears, taking the only known house number with it. The house is still occupied, but now nobody knows its number. That stranded house is a memory leak . If the number vanishes while the house is already returned, and you use it anyway, that is a dangling pointer . (See Memory-leaks-and-dangling-pointers .)
This is the mechanism that makes smart pointers work, so we build it carefully.
Definition Scope and lifetime
Scope = the region of code between a { and its matching } where a name is usable.
Lifetime = the stretch of time an object actually exists.
For a normal stack variable these coincide: it is born at its declaration and dies at the closing } .
Definition Constructor and destructor
A constructor is code that runs automatically when an object is born — to set it up.
A destructor is code that runs automatically when an object dies (reaches its closing }, or is otherwise destroyed) — to clean it up. Its name is the type name with a tilde in front: ~Widget().
Read the tilde ~ as “destructor for” .
Intuition Why the destructor is the whole trick
The destructor is a promise the language keeps for you : it always runs on scope exit — even on an early return, even on a thrown exception. So if we put delete ptr; inside a destructor , the delete becomes impossible to forget. That is exactly what a smart pointer is: a tiny stack object whose destructor deletes the heap object. This whole discipline has a name — RAII (Resource Acquisition Is Initialization).
Copy = make a second, independent thing equal to the first. Both originals survive.
Move = transfer the guts of one thing into another, leaving the source empty (but valid). Only one ends up holding the resource.
Intuition Why the difference is life-or-death for pointers
If you copy a pointer-that-owns, two owners now both believe they must delete the same house → two deletes on one house → double-free (a crash). Move sidesteps this: after the transfer the source is nullptr, so its destructor's delete does nothing. This is the whole reason Move-semantics exists in the smart-pointer story.
std::move and nullptr
nullptr — a special "points at nothing" value. Deleting nullptr is defined to be a harmless no-op.
std::move(x) — does not move anything by itself. It is a cast : it labels x as "you may steal from me", which lets the move-machinery run. Think of it as a green sticker saying “take my guts”.
The parent's shared_ptr rests on one idea: Reference-counting .
Definition Reference count
A reference count is a single integer stored beside the object, recording how many owners currently hold it . Each new owner does count + 1; each departing owner does count − 1. When the count reaches 0 , the last owner deletes the object.
Intuition Why counting solves shared ownership
When nobody can say who dies last, you don't guess — you tally . The object stays alive exactly while at least one owner exists, and dies the instant the last one leaves. This is a lightweight cousin of full Garbage-collection : same goal ("free it when unreachable"), but decided immediately and locally by a counter rather than by a separate collector scanning memory.
Common mistake Counting cannot see cycles
If house A's count is kept above 0 only by house B, and B's count is kept above 0 only by A, then dropping every outside handle still leaves both counts at 1. Neither ever hits 0. That is the cycle leak the parent solves with a weak (non-counting) reference. Full garbage collectors can detect this; plain reference counting cannot.
Definition The plumbing symbols
std:: — the namespace prefix. Read std::x as "the x that lives in the standard library." It stops standard names clashing with yours.
#include <memory> — the line that pulls in the smart-pointer tools before you use them.
make_unique<T>(args) / make_shared<T>(args) — factory functions that do the new, build the object, and wrap it in one step, so no bare new ever floats loose in your code.
<T> — a template parameter : you plug in the type. unique_ptr<int> = "a unique owner of an int."
Intuition Why the whole vault leans on
Rule-of-Zero
Once every resource is held by a smart pointer, your own classes need to write no destructor, copy, or move at all — the smart-pointer members handle it. Writing zero of that boilerplate is the Rule of Zero , the payoff of everything above.
Memory as numbered houses
Constructor and destructor
Each arrow means "you must understand the tail before the head makes sense." Follow any path top-to-bottom and you arrive at the topic with no gaps.
Read the left side, answer out loud, then reveal.
What does a raw pointer T* actually store? Just a number — the address of a house in memory; it says where , never who owns it .
What does *p do, and how is it the reverse of &x? *p reads the value at the address p holds (address → value); &x gives the address of x (value → address).
Why does a heap object leak when its block ends? Only the stack pointer variable dies; the heap house stays, but its address is lost, so nobody can delete it.
When exactly does a destructor run? Automatically the moment the object dies — closing }, early return, or thrown exception — which is why delete inside it can't be forgotten.
Why can two owners copying one raw pointer crash? Both destructors call delete on the same house → double-free .
What does std::move(x) really do? Nothing by itself — it's a cast that labels x as "steal-from-able", enabling the move that empties the source.
When does reference counting delete the object? When the count reaches 0 — the last owner has left.
Why can't plain reference counting free a cycle? A and B keep each other's count at 1, so neither ever reaches 0.
Why prefer make_unique / make_shared over bare new? They allocate + construct + wrap in one call, leaving no raw new to leak and staying exception-safe.