What You'll Learn

    • How new/delete work internally
    • Placement new for custom memory
    • Writing a simple pool allocator
    • When to use custom allocators

    Memory Allocation Internals

    Every new call triggers a chain: the compiler calls operator new, which typically calls malloc, which asks the OS for memory via sbrk or mmap. Understanding this chain lets you write allocators that skip the overhead for hot paths.

    How new and delete Really Work

    When you write int* p = new int(42), three things happen: (1) operator new(sizeof(int)) allocates raw bytes, (2) the constructor runs in that memory, (3) the typed pointer is returned. delete reverses the process — destructor first, then operator delete frees the bytes.

    For arrays, new[] stores the element count just before the first element so delete[] knows how many destructors to call. This is why mixing delete and delete[] is undefined behaviour — the count metadata is missing or misinterpreted.

    Pro Tip: Use new(std::nothrow) to get nullptr on failure instead of a thrown std::bad_alloc exception — useful in embedded systems or tight loops.

    new / delete Mechanics

    Allocate single objects, arrays, and test nothrow allocation

    Try it Yourself »
    C++
    #include <iostream>
    using namespace std;
    
    int main() {
        // Single object allocation
        int* p = new int(42);
        cout << "Value: " << *p << endl;
        cout << "Address: " << p << endl;
        delete p;  // free the memory
    
        // Array allocation
        int* arr = new int[5]{10, 20, 30, 40, 50};
        for (int i = 0; i < 5; i++)
            cout << arr[i] << " ";
        cout << endl;
        delete[] arr;  // free array memory
    
        // new with nothrow — returns nullptr on failure
        int* big = new(nothrow) int
    ...

    Placement new — Construct Without Allocating

    Placement new separates allocation from construction. You provide a pre-allocated buffer, and placement new calls the constructor in that exact location. This is the foundation of every custom allocator, memory pool, and arena.

    The key rule: when you use placement new, you must call the destructor manually. You never call delete on placement-new'd objects because you own the underlying memory separately.

    Common Mistake: Forgetting alignas when using placement new. Misaligned memory causes undefined behaviour on most architectures.

    Placement new

    Construct objects in a pre-allocated stack buffer

    Try it Yourself »
    C++
    #include <iostream>
    #include <new>  // for placement new
    using namespace std;
    
    struct Sensor {
        int id;
        double reading;
        Sensor(int i, double r) : id(i), reading(r) {
            cout << "Sensor " << id << " constructed" << endl;
        }
        ~Sensor() {
            cout << "Sensor " << id << " destroyed" << endl;
        }
    };
    
    int main() {
        // Pre-allocate raw memory buffer
        alignas(Sensor) char buffer[sizeof(Sensor) * 3];
    
        // Construct objects in the buffer (placement new)
        Sensor* s1 
    ...

    Writing a Simple Pool Allocator

    A pool allocator pre-allocates a block of fixed-size slots. Allocation is O(1) — just pop from the free list. Deallocation is O(1) — push back onto the free list. No fragmentation, no system calls, no overhead.

    Pool allocators are ideal when you create and destroy many objects of the same type — particle systems, network packets, game entities. The standard allocator's general-purpose overhead becomes the bottleneck in these scenarios.

    Pool Allocator

    Build a simple free-list pool allocator from scratch

    Try it Yourself »
    C++
    #include <iostream>
    #include <vector>
    #include <memory>
    using namespace std;
    
    // Simple pool allocator — pre-allocates a block
    template<typename T, size_t PoolSize = 64>
    class PoolAllocator {
        union Slot { T value; Slot* next; };
        Slot pool[PoolSize];
        Slot* freeList;
    public:
        PoolAllocator() {
            freeList = &pool[0];
            for (size_t i = 0; i < PoolSize - 1; i++)
                pool[i].next = &pool[i + 1];
            pool[PoolSize - 1].next = nullptr;
        }
        T* allocate() {
       
    ...

    Quick Reference

    TechniqueUse Case
    new / deleteGeneral heap allocation
    new(nothrow)Allocation without exceptions
    placement newConstruct in pre-allocated memory
    Pool allocatorMany same-size allocations
    Arena allocatorBulk allocate, free all at once

    Lesson Complete!

    You now understand how C++ allocates memory internally and can write custom allocators for performance-critical code.

    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