So how does delete[] know how much to deallocate? When you allocate an array using new[], the runtime needs to track its size so that delete[] can correctly free the allocated memory. The runtime adds a metadata before the actual array data.
1. How new[] and delete[] work internally
Step 1: Allocation with new[]
When you allocate an array:
int* arr = new int[5]; // Allocates space for 5 integers
- The runtime stores metadata (usually size information) before the actual array.
- The pointer returned by
new[]is adjusted to point to the first array element.
Step 2: Deallocation with delete[]
When you call:
delete[] arr;
- The runtime looks at the hidden metadata stored before
arrto determine how many elements were allocated. - It correctly calls the destructor (if needed) for each element.
- Finally, it frees the entire block of memory.
2. Where Is the Array Size Stored?
Common Implementations
Most C++ implementations store the array size just before the allocated memory block.
Example (assuming 4-byte metadata):
Memory Layout (simplified for new int[5])
-------------------------------------------------
| Metadata (size = 5) | arr[0] | arr[1] | ... | arr[4] |
-------------------------------------------------
↑
arr points here
When you call delete[] arr;, it:
- Looks at the metadata before
arrto get the size (5in this case). - Calls the destructor for each element (if necessary).
- Frees the memory block.
3. Example: Tracking Array Size Manually
We can simulate this behavior manually:
#include <iostream>
void* myAlloc(size_t count) {
size_t* ptr = (size_t*)malloc(sizeof(size_t) + count * sizeof(int));
*ptr = count; // Store size at the beginning
return (void*)(ptr + 1); // Return the address after metadata
}
void myDealloc(void* mem) {
size_t* ptr = ((size_t*)mem) - 1; // Access metadata
std::cout << "Freeing array of size: " << *ptr << std::endl;
free(ptr); // Free the entire allocated block
}
int main() {
int* arr = (int*)myAlloc(5);
myDealloc(arr);
return 0;
}
✅ Output:
Freeing array of size: 5
This shows how delete[] can determine the number of elements.
4. Does delete[] Call Destructors?
Yes! When you use new[] for an array of objects, delete[] ensures that all destructors are called.
Example:
#include <iostream>
class Test {
public:
Test() { std::cout << "Constructor\n"; }
~Test() { std::cout << "Destructor\n"; }
};
int main() {
Test* arr = new Test[3];
delete[] arr;
}
✅ Output:
Constructor
Constructor
Constructor
Destructor
Destructor
Destructor
Why? Because delete[] retrieves the stored array size and calls the destructor for each element.
5. Summary
| Action | How It Works |
|---|---|
new[] allocation | Stores array size before the returned pointer |
| Pointer returned | Points to the first element, not the metadata |
delete[] behavior | Reads stored size, calls destructors (if needed), then frees memory |
| Raw pointers | delete without [] on arrays causes memory leaks |
Summary:
✔ delete[] knows how much to deallocate because new[] stores hidden metadata before the array.
✔ The compiler/runtime retrieves this metadata to correctly free the memory and call destructors.
✔ Never use delete instead of delete[] for arrays, or you’ll cause undefined behavior!