Visual walkthrough — Rule of Zero — prefer compiler-generated specials
We build every term before we use it. Let's go.
Step 1 — What is an object, really?
WHAT. We picture a class Bad with two members: a number n, and a pointer p that points at a separate block of memory (the actual array).
WHY. Copying is about memory. Before we can ask "what does copying do?", we must see clearly what lives inside the object versus what lives outside it, connected only by an arrow.
PICTURE. The object is the small teal box. Its member p is not the array — it is an arrow pointing to the array (the orange strip) that lives elsewhere.

Step 2 — What does "copy" mean by default?
WHAT. We make a second object Bad b = a;. The compiler copies n (a number → fine) and copies p (an arrow → it copies the arrow, not what it points to).
WHY. We use memberwise copy because it is the compiler's free default. Understanding precisely what it does — copy the arrow, not the array — is what reveals the bug.
PICTURE. After the copy, there are two boxes but still one orange array. Both arrows land on the same strip.

Step 3 — Why the shallow copy explodes
WHAT. Both a and b die at the end of the scope. Each runs its destructor. Each says delete[] p. But both ps point at the same array — so it is freed twice.
WHY. This is the concrete disaster that forces your hand. The moment your class holds a raw owning pointer and frees it in a destructor, the free default copy becomes a bug.
PICTURE. Two destructors, two delete[] bolts, both striking the same orange array — the second strike hits already-freed memory.

Step 4 — The cascade: one function drags in five
WHAT. To fix the double-free you write a deep copy (make a fresh array). But C++ has a rule: declaring a destructor or a copy stops the compiler from generating the move operations. So to keep fast moves you must write those too. One repair pulls in four more.
WHY. This shows why raw ownership is so costly: correctness is not one function, it is a tangle of five, each with its own exception-safety and self-assignment pitfalls (see Copy-and-swap idiom and noexcept and move operations).
PICTURE. A domino chain: ~Bad topples "shallow copy is wrong" → topples "write copy ctor + copy assign" → topples "moves no longer generated" → topples "write move ctor + move assign."

Step 5 — The escape: make the member manage itself
WHAT. Replace the raw int* p with std::vector<int> p. Now the member is not a bare arrow — it is a self-contained owner that carries its own correct copy (deep), move (steal), and destroy (free).
WHY. We choose an RAII member because it makes Step 2 (shallow copy) impossible and Step 3 (double-free) impossible. There is no raw pointer for the compiler to mis-handle.
PICTURE. The teal box now contains a self-managing capsule. Copying the object asks the capsule to copy itself — and the capsule makes a brand-new orange array. Two boxes, two arrays, no sharing.

Step 6 — The dominoes never fall: zero functions
WHAT. Because the RAII member never breaks the default copy, you never need a destructor. Because you write no destructor and no copy, the compiler keeps generating all five specials — every one correct.
WHY. This is the payoff. The cascade of Step 4 is inverted: instead of "one written function forces five," now "zero written functions gives five correct ones for free."
PICTURE. The same domino chain as Step 4, but the first domino is a solid wall — nothing topples. All five specials stand up on their own, glowing green.

Step 7 — Edge case: unique_ptr gives move-only for free
WHAT. Put a std::unique_ptr<Shape> in a class. unique_ptr is not copyable. Memberwise copy would need to copy the member — which is impossible — so the compiler implicitly deletes the class's copy. The class becomes move-only, automatically.
WHY. Rule of Zero doesn't just hand you copying blindly — it hands you the member's semantics. Choosing unique_ptr is choosing move-only, with zero = delete written by you.
PICTURE. Copy arrow struck through in plum ("deleted, because the member can't be copied"); move arrow glowing teal ("allowed — the single arrow simply moves to the new box, leaving the old empty").

Step 8 — Edge case: no wrapper exists → build exactly one
WHAT. A raw OS handle like FILE* has no standard RAII wrapper. So you write one tiny class, FileHandle, that owns it (the only class with hand-written specials). Every business class then holds a FileHandle and stays at Rule of Zero.
WHY. You quarantine the danger. The manual, error-prone ownership lives in one small, audited box — this is the Single Responsibility Principle. Everything else composes cleanly.
PICTURE. A red "manual ownership" zone containing exactly one box (FileHandle); outside it, a calm cream zone of many boxes (Logger, ...) each writing zero specials, all reaching through FileHandle.

The one-picture summary
Everything above, compressed: the raw-pointer world topples into a five-function cascade; the RAII world stands with zero.

Recall Feynman: the whole walkthrough in plain words
Think of an object as a box. Some things sit inside the box (a number). Some things are just an arrow pointing to stuff outside (a raw pointer to an array). When the computer copies a box the lazy way, it copies the arrow but not the array — so now two boxes point at one array. When both boxes are thrown away, each tries to delete that one array, and deleting it twice crashes. To stop the crash you write a "clean-up" function — but that one function forces you to write a proper copy, and writing a copy makes the fast "move" versions disappear, so you write those too: five fiddly functions to babysit one arrow. The escape is to stop using a bare arrow. Use a smart member — a vector, a string, a unique_ptr — that already knows how to copy itself deeply, move itself cheaply, and clean itself up. Now the lazy copy is never wrong, you never need a clean-up function, and the compiler happily gives you all five correct functions for free. That's Rule of Zero: the best special functions are the ones you never had to write.
Recall Quick self-test
Why does one hand-written destructor force the Rule of Five? ::: It makes the default shallow copy a double-free, so you must write deep copy + assign; and declaring a destructor stops the compiler generating the move ops, so you write those too.
What makes a unique_ptr member give you a move-only class with zero effort? ::: unique_ptr is non-copyable, so memberwise copy is impossible and the class's copy is implicitly deleted; move still works, transferring ownership.
When there is no RAII wrapper (e.g. FILE*), what does Rule of Zero advise? ::: Write exactly one small RAII class that owns the raw handle; every other class holds it and stays at zero specials.