delete [] question

Started by
11 comments, last by Kippesoep 19 years, 3 months ago
let Derived be a class that derives from class Base. now if I do this: Base *b = new Derived[10]; and later I do this: delete [] b; will the above delete[] properly release the memory allocated by new? how does it know to delete 10 Base or 10 Derived?
Advertisement
Quote:Original post by digitalfreak
will the above delete[] properly release the memory allocated by new?


The memory allocated for the array itself will be deallocated correctly. But...

Quote:how does it know to delete 10 Base or 10 Derived?


It doesn't know.

Treating arrays polymorphically is a big no-no.

The compiler uses the (static) type of the pointer to determine how far aparts objects are located in the array. If Base and Derived have different sizes, delete[] will use the wrong offsets when calling the destructors.

For the destructors themselves, it relies on the virtual function table. Which is why, if you are going to delete a Derived object through a pointer to Base, Base need a virtual destructor. Of course, it is contingent on finding the objects in the expected places (see previous paragraph).
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
then what's the solution?
delete [] (Derived *)b; //does this work?
No, it doesn't. You should either:

Derived *d = new Derived [10];...delete [] d;


or:

Base **b = new Base * [10];for (int i = 0; i < 10; ++i) b  = new Derived;...for (int i = 0; i < 10; ++i) delete b ;delete [] b;
Kippesoep
Quote:Original post by digitalfreak
then what's the solution?
delete [] (Derived *)b; //does this work?

Actually this will work, but you shouldn't do it. It will work only if you know b *definately* points to an array of Derived.....in which case you should be using Kippesoep's first example anyway.
"Voilà! In view, a humble vaudevillian veteran, cast vicariously as both victim and villain by the vicissitudes of Fate. This visage, no mere veneer of vanity, is a vestige of the vox populi, now vacant, vanished. However, this valorous visitation of a bygone vexation stands vivified, and has vowed to vanquish these venal and virulent vermin vanguarding vice and vouchsafing the violently vicious and voracious violation of volition. The only verdict is vengeance; a vendetta held as a votive, not in vain, for the value and veracity of such shall one day vindicate the vigilant and the virtuous. Verily, this vichyssoise of verbiage veers most verbose, so let me simply add that it's my very good honor to meet you and you may call me V.".....V
hi, I've just checked this with vc++ 2003.net
I watched the memory holding the array before and after delete, I could tell that the exact amount of memory that were allocted by new were released, no matter I use (Derived *) coercion or not.

so what does this imply?
This would imply either Base and Derived are the same size, or you code was simple enough for the compiler to know what to do.

What were the sizes of Base and Derived?
According to the C++ standard (Section 5.3.5 paragraph 3 for those of you keeping track at home) using delete [] on a pointer whose dynamic type differs from its static type has undefined behaviour. And undefined behaviour can mean cleaning up the memory properly and calling all of right the destructors.

It can also mean segfaulting and launching nuclear missiles at a cow ranch in Alaska.

However, judging from the generated disassembly, in a new project, with default compiler settings, etc. as long as the base class's destructor is virtual, MSVC 7.1 will free the proper amount of memory and call the right destructors. It's still not something I would rely on.
As André LaMothe says on a similar subject, Although not COM objects are currently released automatically, not releasing COM objects might in the future create wormholes next to your head, to let's be on the safe side and release our COM objects.
While the behavior of C++ when deleting a derived object through a base pointer may be undefined, the actual memory deallocation should work correctly. This does not, however, mean that the right destructors will be called.

C++ depends on a virtual memory allocator to satisfy requests for memory. The memory allocator returns blocks of at least the requested size or returns an error. Often, the region of memory returned is actually larger than requested so that it ends on a specific boundary (such as 8 bytes). The memory allocator tags each chunk of memory with a size value so that it knows how large each chunk is. When delete is called, the size value in the tag is used to determine how much memory must be deallocated not the size of the base or the derived object.

After the memory is deallocated, the chunk is placed in a free list of available blocks. In the GNU libc malloc implementation, each chunk of memory is tagged with the size of the current and the previous blocks. When the chunk is unallocated, a forward and back pointer are also stored in the chunk (after the size values) so that the chunk can be stored in doubly-linked list of free blocks. On allocation, the pointer values are not needed (it's not in the list anymore) so they are considered to be a part of the data region of the chunk.

Implementations of new and delete are more complicated than malloc and free because they must determine which constructors and destructors to call but the actual memory allocation should be very similar.

Steven

This topic is closed to new replies.

Advertisement