Is memory released when a destructor is called or when `delete` is called?
Assume you have an 开发者_C百科object of class Fool
.
class Fool
{
int a,b,c;
double* array ;
//...
~Fool()
{
// destroys the array..
delete[] array ;
}
};
Fool *fool = new Fool() ;
Now, I know you shouldn't, but some fool calls the destructor on fool
anyway. fool->~Fool();
.
Does that mean fool
's memory is freed, (ie a,b,c are invalid) or does that mean only whatever deallocations in ~Fool()
function occur (ie the array is deleted only?)
So I guess my question is, is a destructor just another function that gets called when delete
is called on an object, or does it do more?
If you write
fool->~Fool();
You end the object's lifetime, which invokes the destructor and reclaims the inner array
array. The memory holding the object is not freed, however, which means that if you want to bring the object back to life using placement new:
new (fool) Fool;
you can do so.
According to the spec, reading or writing the values of the fields of fool
after you explicitly invoke the destructor results in undefined behavior because the object's lifetime has ended, but the memory holding the object should still be allocated and you will need to free it by invoking operator delete
:
fool->~Fool();
operator delete(fool);
The reason to use operator delete
instead of just writing
delete fool;
is that the latter has undefined behavior, because fool
's lifetime has already ended. Using the raw deallocation routine operator delete
ensures that the memory is reclaimed without trying to do anything to end the object's lifetime.
Of course, if the memory for the object didn't come from new
(perhaps it's stack-allocated, or perhaps you're using a custom allocator), then you shouldn't use operator delete
to free it. If you did, you'd end up with undefined behavior (again!). This seems to be a recurring theme in this question. :-)
Hope this helps!
The destructor call does just that, it calls the destructor. Nothing more and nothing less. Allocation is separate from construction, and deallocation from destruction.
The typical sequence is this:
1. Allocate memory
2. Construct object
3. Destroy object (assuming no exception during construction)
4. Deallocate memory
In fact, if you run this manually, you will have to call the destructor yourself:
void * addr = ::operator new(sizeof(Fool));
Fool * fp = new (addr) Fool;
fp->~Fool();
::operator delete(addr);
The automatic way of writing this is of course Fool * fp = new Fool; delete fp;
. The new
expression invokes allocation and construction for you, and the delete
expression calls the destructor and deallocates the memory.
Does that mean fool's memory is freed, (ie a,b,c are invalid) or does that mean only whatever deallocations in ~Fool() function occur (ie the array is deleted only?)
Fool::~Fool()
has zero knowledge about whether the Fool
instance is stored in dynamic storage (via new
) or whether it is stored in automatic storage (i.e. stack objects). Since the object ceases to exist after the destructor is run, you can't assume that a
, b
, c
and array
will stay valid after the destructor exits.
However, because Fool::~Fool()
knows nothing about how the Fool
was allocated, calling the destructor directly on a new
-allocated Fool
will not free the underlying memory that backs the object.
You should not access a
, b
, and c
after the destructor is called, even if it's an explicit destructor call. You never know what your compiler puts in your destructor that might make those values invalid.
However, the memory is not actually freed in the case of an explicit destructor call. This is by design; it allows an object constructed using placement new
to be cleaned up.
Example:
char buf[sizeof (Fool)];
Fool* fool = new (buf) Fool; // fool points to buf
// ...
fool->~Fool();
The easiest place to see that the destructor is distinct from deallocation via delete
is when the allocation is automatic in the first place:
{
Fool fool;
// ~Fool called on exit from block; nary a sign of new or delete
}
Note also the STL containers make full use of the explicit destructor call. For example, a std::vector<>
treats storage and contained objects lifetime quite separately.
精彩评论