Memory — storage and lifetime are not the same thing
C++ is one of the few modern languages where the developer decides where an object lives, who is responsible for it, and when it dies. No garbage collector, no "it'll be freed eventually". An ownership or lifetime mistake in C++ does not raise an exception — it turns into Undefined Behavior: silent data corruption, an intermittent crash, or a bug that only reproduces in production.
The idea C++ insists on separating from other languages: storage (the raw bytes where an object may live) and lifetime (the period during which an object of that type actually exists there) are different things. Bytes can remain after the object is destroyed; a virtual address can be reserved before the OS maps a physical page in. So "it didn't crash" is not a correctness argument.
The standard gives four storage durations (automatic / static / thread / dynamic), two non-owning handles (T*, T&), and explicit ways to express ownership (unique_ptr, shared_ptr, RAII objects). The platform adds the real process map (stack, heap, sections, virtual pages) and alignment rules. The full picture lives in the layers below.
Topic map
- C++ memory model — storage / lifetime / scope / ownership, the four storage durations, why this is not the same as stack/heap.
- Virtual memory and OS pages — process map (stack / heap / .bss / .data / .rodata / .text), ASLR, mmap, pages and page faults.
- Pointers and references —
T*vsT&, how they differ, and why neither one carries ownership on its own. - Dynamic memory —
new/delete,malloc/free, RAII, smart pointers; whynew[]must pair withdelete[]. - Alignment —
alignof,alignas, padding inside structs, how field order changessizeof. - std::string storage and SSO — where the object lives, where the buffer lives, what Small String Optimization actually does.
- volatile and const volatile — what
volatiledoes not do; why it is not a replacement forstd::atomic. - Memory optimization — arenas, pools, cache-line alignment, false sharing, SoA vs AoS.
Common traps
| Mistake | Consequence |
|---|---|
Mixing new/delete with malloc/free | UB — different API families, different runtime structures |
delete on an array allocated with new[] | UB; destructors for the rest of the array do not run |
Two shared_ptr built from the same raw pointer | Double free — both control blocks think they own the object |
Treating volatile as thread-safe | Races remain; you need std::atomic or a mutex |
| Returning a pointer or reference to a local | Dangling — UB on use, sometimes appears to "work" |
| Relying on use-after-free to crash | The allocator kept the block in a free-list — UB reads silently |
Depending on exact SSO size or .data/.bss layout | Platform-dependent; breaks across stdlib/compiler upgrades |
Passing T* without spelling ownership in the type | The interface is invisible on review: nobody knows who deletes |
new T[n] instead of std::vector / unique_ptr<T[]> | Manual delete[] required, and any thrown exception leaks |
| Capturing a reference to a temporary | Dangling after end-of-expression; lifetime extension only for named const& |
Interview relevance
Memory comes up on almost every C++ interview. The interviewer is not checking whether you know the word "heap" — they are checking whether you can reason about lifetime and ownership.
Typical checks:
- Storage duration, lifetime, and scope — these are different concepts, not synonyms.
new/deletevsmalloc/free— constructor/destructor vs raw bytes, never mix.- Why RAII and smart pointers solve ownership where a raw pointer cannot.
- Use-after-free as UB even when the code "works".
- Why
volatiledoes not replacestd::atomicfor thread synchronization. - Padding,
alignof, and how field order changessizeof. - Where
std::stringlives and what SSO does; whereconst char*literals live. - What happens when you build two
shared_ptrfrom the same raw pointer.
Common wrong answer: "volatile int is a thread-safe counter, like Atomic in Java/C#". That is exactly the opening for explaining that volatile in C++ exists for memory-mapped IO and signal handlers, and gives no synchronization guarantees between threads.