Courses/C++/Modern C++ Memory Model

    Lesson 15 • Advanced

    Modern C++ Memory Model

    Understand how C++ organises memory into segments, how smart pointers work internally, and how object lifetimes are managed.

    What You'll Learn

    • Memory layout: text, data, stack, heap
    • How smart pointers are implemented internally
    • Object lifetime rules (stack, heap, static)
    • Control blocks and reference counting

    Memory Layout of a C++ Program

    When your C++ program runs, the OS divides its address space into distinct segments:

    SegmentContainsGrows
    TextCompiled machine codeFixed
    DataGlobal & static variablesFixed
    BSSUninitialised globals (zeroed)Fixed
    HeapDynamic allocations (new)↑ Upward
    StackLocal variables, function calls↓ Downward

    The stack and heap grow toward each other. A stack overflow occurs when the stack grows past its limit (usually 1-8 MB). Deep recursion is the most common cause.

    Memory Layout

    Inspect addresses to see where different variables live in memory

    Try it Yourself »
    C++
    #include <iostream>
    using namespace std;
    
    // Global variable — stored in data segment
    int globalVar = 42;
    
    // Static variable — also data segment
    static int staticVar = 100;
    
    void demonstrateLayout() {
        // Local variable — stack
        int stackVar = 10;
        
        // Dynamic allocation — heap
        int* heapVar = new int(99);
        
        cout << "=== Memory Layout ===" << endl;
        cout << "Global  (data):  " << &globalVar << " = " << globalVar << endl;
        cout << "Static  (data):  " << &staticVar <<
    ...

    Smart Pointer Internals

    unique_ptr is essentially a raw pointer with a destructor — zero overhead at runtime. It's the same size as a raw pointer. shared_ptr is larger because it stores two pointers: one to the object and one to the control block.

    The control block contains the strong reference count, weak reference count, deleter, and allocator. make_shared is more efficient because it allocates the object and control block in a single memory allocation.

    Smart Pointer Internals

    Explore sizes, reference counts, and custom deleters

    Try it Yourself »
    C++
    #include <iostream>
    #include <memory>
    using namespace std;
    
    class Widget {
        int id;
    public:
        Widget(int i) : id(i) { cout << "Widget " << id << " born" << endl; }
        ~Widget() { cout << "Widget " << id << " died" << endl; }
        int getId() const { return id; }
    };
    
    int main() {
        cout << "=== Smart Pointer Internals ===" << endl;
        
        // unique_ptr: just a pointer + deleter — same size as raw pointer
        unique_ptr<Widget> u1 = make_unique<Widget>(1);
        cout << "sizeof(unique_ptr):
    ...

    Object Lifetime

    Watch constructors and destructors fire in different lifetime scenarios

    Try it Yourself »
    C++
    #include <iostream>
    #include <vector>
    #include <memory>
    using namespace std;
    
    class Logger {
        string tag;
    public:
        Logger(string t) : tag(t) {
            cout << "[" << tag << "] Constructor called" << endl;
        }
        Logger(const Logger& other) : tag(other.tag + "_copy") {
            cout << "[" << tag << "] Copy constructor" << endl;
        }
        Logger(Logger&& other) noexcept : tag(move(other.tag) + "_moved") {
            cout << "[" << tag << "] Move constructor" << endl;
        }
        ~Logger() {
      
    ...

    Common Mistakes

    ⚠️ Stack overflow from recursion: Each function call uses stack space. Infinite or deep recursion will exhaust the stack.

    ⚠️ Storing addresses of temporaries: A temporary object is destroyed at the end of its statement. Pointers to it become dangling immediately.

    ⚠️ Assuming static order: Static objects in different translation units have no guaranteed construction order. This is the "static initialization order fiasco."

    Pro Tips

    💡 Use make_shared over new: make_shared<T>() does one allocation instead of two, improving cache locality and performance.

    💡 emplace_back over push_back: emplace_back constructs objects in-place inside the container, avoiding extra moves.

    💡 Reserve vector capacity: Call vec.reserve(n) before inserting to avoid repeated reallocations and moves.

    📋 Quick Reference

    ConceptDetail
    Stack limit~1-8 MB (OS-dependent)
    sizeof unique_ptrSame as raw pointer (8 bytes on 64-bit)
    sizeof shared_ptr2 × pointer size (16 bytes on 64-bit)
    Destruction orderLIFO for stack, manual for heap
    Static lifetimeConstructed before main, destroyed after

    Lesson Complete!

    You now understand how C++ organises memory, how smart pointers work under the hood, and how object lifetimes are managed. Next up: Advanced OOP — virtual tables, polymorphic dispatch, and multi-level inheritance.

    Sign up for free to track which lessons you've completed and get learning reminders.

    Previous

    Cookie & Privacy Settings

    We use cookies to improve your experience, analyze traffic, and show personalized ads. You can manage your preferences below.

    By clicking "Accept All", you consent to our use of cookies for analytics and personalized advertising. You can customize your preferences or reject non-essential cookies.

    Privacy PolicyTerms of Service