mar 9, 2023
Most of the time you just write:
Base* ptr = new Derived();
delete ptr; // virtual destructor cleans up Derived as expected
delete does two things: it runs the destructor and it calls the deallocation function that matches the allocation used.
If you add the global-scope qualifier:
::delete ptr; // forces the global operator delete
you bypass overload resolution and ask for the plain global operator delete. On GCC and Clang that means the sized-delete overload is selected with sizeof(Base), so only the base-class portion of the allocation is released. MSVC still frees the full block because it passes the real size through the destructor.
Pretty much only when you're writing allocator glue or testing something very low-level and you need to skip class-specific or placement deletes. For everyday code, stick with plain delete.
| expression | what happens | safe |
|---|---|---|
delete ptr; |
runs dynamic destructor, calls matching deallocator | yes |
::delete ptr; |
forces global deallocator; size may be wrong on gcc/clang | no |
Bottom line: use plain delete unless you have a concrete, allocator-level reason to do otherwise.