I tried to call ::delete
for a class in the operator delete
of it. But the destructor is not called.
I defined a class MyClass
whose operator delete
has been overloaded. The global operator delete
is also overloaded. The overloaded operator delete
of MyClass
will call the overloaded global operator delete
.
class MyClass
{
public:
MyClass() { printf("Constructing MyClass...\n"); }
virtual ~MyClass() { printf("Destroying MyClass...\n"); }
void* operator new(size_t size)
{
printf("Newing MyClass...\n");
void* p = ::new MyClass();
printf("End of newing MyClass...\n");
return p;
}
void operator delete(void* p)
{
printf("Deleting MyClass...\n");
::delete p; // Why is the destructor not called here?
printf("End of deleting MyClass...\n");
}
};
void* operator new(size_t size)
{
printf("Global newing...\n");
return malloc(size);
}
void operator delete(void* p)
{
printf("Global deleting...\n");
free(p);
}
int main(int argc, char** argv)
{
MyClass* myClass = new MyClass();
delete myClass;
return EXIT_SUCCESS;
}
The output is:
Newing MyClass...
Global newing...
Constructing MyClass...
End of newing MyClass...
Constructing MyClass...
Destroying MyClass...
Deleting MyClass...
Global deleting...
End of deleting MyClass...
Actual:
There is only one call to the destructor before calling the overloaded operator delete
of MyClass
.
Expected:
There are two calls to the destructor. One before calling the overloaded operator delete
of MyClass
. Another before calling the global operator delete
.
Yes, delete[] guarantees destructors are called on every object.
There are two reasons that your destructors aren't being called, one is as kishor8dm pointed out that you are using the operator "new" and because of that the "delete" command must be called explicitly.
Basically destructor is about the death of objects and deletion is about the freeing of dynamically allocated memory, but the two actions are often intertwined.
When an object goes out of scope normally, or a dynamically allocated object is explicitly deleted using the delete keyword, the class destructor is automatically called (if it exists) to do any necessary clean up before the object is removed from memory.
It shouldn't call the deleting destructor, because it does not actually delete an object from the heap. It is also interesting to examine how the destructor (s) of Animal look, since unlike Sheep, Animal does not define a custom operator delete: As expected, the destructor of Animal calls the global ::operator delete.
There are two reasons that your destructors aren't being called, one is as kishor8dm pointed out that you are using the operator "new" and because of that the "delete" command must be called explicitly. The other is because the objects are inside of your main () function and as such their scope is the same as the program itself.
It's called the deleting destructor and its existence is described by the Itanium C++ ABI: deleting destructor of a class T - A function that, in addition to the actions required of a complete object destructor, calls the appropriate deallocation function (i.e,. operator delete) for T. The ABI goes on to provide more details:
Since the destructor is virtual, what ends up called eventually is the destructor for the dynamic type of the object. In our example this would be the destructor of Sheep, which can call the right operator delete since it's in the same static scope. However, as the ABI says, such classes need two destructors.
Your class-specific overloads are done incorrectly. This can be seen in your output: the constructor is called twice!
In the class-specific operator new
, call the global operator directly:
return ::operator new(size);
Similarly, in the class-specific operator delete
, do:
::operator delete(p);
Refer to the operator new
reference page for more details.
You are misusing operator new
and operator delete
. These operators are allocation and deallocation functions. They are not responsible for constructing or destructing objects. They are responsible only for providing the memory in which the object will be placed.
The global versions of these functions are ::operator new
and ::operator delete
.
::new
and ::delete
are new/delete-expressions, as are new
/delete
, differing from those, in that ::new
and ::delete
will bypass class-specific operator new
/operator delete
overloads.
The new/delete-expressions construct/destruct and allocate/deallocate (by calling the appropriate operator new
or operator delete
before construction or after destruction).
Since your overload is only responsible for the allocation/deallocation part, it should call ::operator new
and ::operator delete
instead of ::new
and ::delete
.
The delete
in delete myClass;
is responsible for calling the destructor.
::delete p;
does not call the destructor because p
has type void*
and therefore the expression cannot know what destructor to call. It will probably call your replaced ::operator delete
to deallocate the memory, although using a void*
as operand to a delete-expression is ill-formed (see edit below).
::new MyClass();
calls your replaced ::operator new
to allocate memory and constructs an object in it. The pointer to this object is returned as void*
to the new-expression in MyClass* myClass = new MyClass();
, which will then construct another object in this memory, ending the lifetime of the previous object without calling its destructor.
Edit:
Thanks to @M.M's comment on the question, I realized that a void*
as operand to ::delete
is actually ill-formed. ([expr.delete]/1) However, the major compilers seem to have decided to only warn about this, not error. Before it was made ill-formed, using ::delete
on a void*
had already undefined behavior, see this question.
Therefore, your program is ill-formed and you don't have any guarantee that the code actually does what I described above if it still managed to compile.
As pointed out by @SanderDeDycker below his answer, you also have undefined behavior because by constructing another object in the memory that already contains a MyClass
object without calling that object's destructor first you are violating [basic.life]/5 which forbids doing so if the program depends on the destructor's side effects. In this case the printf
statement in the destructor has such a side effect.
See CPP Reference:
operator delete
,operator delete[]
Deallocates storage previously allocated by a matching
operator new
. These deallocation functions are called by delete-expressions and by new-expressions to deallocate memory after destructing (or failing to construct) objects with dynamic storage duration. They may also be called using regular function call syntax.
Delete (and new) are only responsible for the 'memory management' part.
So it is clear and expected that the destructor is only called once - to clean up the instance of the object. Would it be called twice, every destructor would have to check if it had already been called.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With