Skip to content

36 Intermediate The Hundred

The delete That Freed One Object

three objects in, one funeral

#include <iostream>

struct Noisy {
    int id = 0;
    ~Noisy() { std::cout << "  ~Noisy #" << id << std::endl; }
};

int main() {
    Noisy* batch = new Noisy[3];
    for (int i = 0; i < 3; ++i)
        batch[i].id = i;

    std::cout << "cleaning up..." << std::endl;
    delete batch;   // one allocation, one delete
    std::cout << "done" << std::endl;
}

Run it. How many destructors run?

Answer

One — and the program never reaches done. Typical output (GCC 11 / glibc on x86-64 Linux, identical from -O0 to -O3):

cleaning up...
  ~Noisy #0
munmap_chunk(): invalid pointer
Aborted (core dumped)

Why

new Noisy[3] quietly over-allocates: for a type with a non-trivial destructor it stashes the element count in a cookie just before the address it returns, so delete[] knows how many destructors to run. Plain delete has never heard of cookies — it destroys one object, then hands the allocator a pointer 8 bytes past the block's start: undefined behavior, which glibc catches here, though another allocator might corrupt the heap in silence. Trivially destructible types need no cookie, so int* p = new int[3]; delete p; usually appears to work — which is how the habit survives to meet a class with a destructor.

GCC catches this one with no -W flags at all (-Wfree-nonheap-object is on by default): "'operator delete' called on pointer ... with nonzero offset 8", and that offset is the cookie. Hide the new[] behind a factory in another file and the warning goes quiet while the abort stays.

The fix

Match the shape of the delete to the shape of the new — or write neither:

delete[] batch;                              // ~Noisy #2, #1, #0, then "done"
std::vector<Noisy> batch(3);                 // better: nothing left to forget
auto batch = std::make_unique<Noisy[]>(3);   // owning array, destroys with delete[]

Takeaway: the brackets are part of the operator — new[] pairs with delete[], always.

Try it: g++ -std=c++17 main.cpp -o demo && ./demo

Open in Compiler Explorer ↗ Quiz this entry