Placement New (Static Allocation Constructor)

cardimg

Standard new allocates memory on the Heap and then constructs the object.

Placement New separates these steps. It allows you to construct an object in a pre-allocated memory buffer (Stack, Static, or a specific hardware address) that you explicitly provide.

This is the standard way to use C++ objects in strict embedded systems where the Heap is banned.

Syntax & Usage

1. Basic Setup

You need a memory buffer (usually a byte array) large enough to hold the object.

Note: You must include <new> to use this.

#include <new> 

class Sensor {
public:
    int id;
    Sensor(int i) : id(i) { printf("Init\n"); }
    ~Sensor() { printf("De-init\n"); }
};

// 1. Allocate raw memory (Static buffer)
// 'alignas' prevents bus faults by ensuring correct memory alignment
alignas(Sensor) uint8_t buffer[sizeof(Sensor)];

int main() {
    // 2. Use Placement New
    // Syntax: new (address) Type(args);
    Sensor* s = new (buffer) Sensor(10); 

    // s->id is now 10, stored inside 'buffer'
}

2. Manual Destruction

Because you didn't allocate memory with standard new, you cannot use standard delete. You must call the destructor manually.

// ❌ delete s;      // CRASH! Tries to free static memory to the heap manager.
s->~Sensor();        // ✅ Correct: Cleans up the object, leaves memory alone.

Memory Visualization

Standard new finds a random spot in the Heap. Placement new puts the object exactly where you say.

Standard new Sensor()Placement new (buf) Sensor()
Step 1: Ask Heap Manager for sizeof(Sensor) bytes.Step 1: User provides address of buffer.
Step 2: Construct object in that heap memory.Step 2: Construct object in buffer.
Result: Pointer to dynamic heap memory.Result: Pointer to your static buffer.

Relevance in Embedded/Firmware

1. No Heap Required

You can have full C++ objects (with constructors, v-tables, and inheritance) stored entirely in Static Memory (.bss). This makes your firmware deterministic and fragmentation-free, satisfying safety standards (MISRA, ISO26262).

2. Memory-Mapped Objects

You can place an object directly onto a hardware register or a specific memory region (e.g., backup SRAM or shared memory between cores).

// Construct a Driver object exactly at the hardware address 0x40005000
UART_Driver* uart1 = new (reinterpret_cast<void*>(0x40005000)) UART_Driver();

3. Object Pools

If you need to create/destroy objects frequently (e.g., Network Packets), use a pre-allocated array of buffers (a Pool). Use placement new to construct a packet in slot 0, manually destroy it, and then reuse slot 0 for a new packet later.

Common Pitfalls (Practical Tips)

PitfallDetails
❌ Memory Alignment

If your buffer is just uint8_t buf[100];, it might start at address 0x...01. If you place an object containing int (4 bytes) there, accessing it causes a Hard Fault on many ARM Cortex-M processors.

Fix: Always use alignas(Type) or __attribute__((aligned(4))) on the buffer.

❌ Using deleteCalling delete on a placement-new pointer will confuse the memory manager (allocator), likely causing a crash or heap corruption. Always use ptr->~Class().
❌ Buffer OverrunYou must ensure sizeof(buffer) >= sizeof(Object). The compiler won't warn you if the buffer is too small, leading to memory corruption of adjacent variables.
✅ Reusing MemoryPlacement new is perfect for "Union-like" behavior where different types of objects reuse the same memory chunk at different times to save RAM.

 

 

 

 

Concept understood? Let's apply and learn for real

Practice now