Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the destructor not called in operator delete?

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.

like image 976
expinc Avatar asked Oct 21 '19 09:10

expinc


People also ask

Does operator delete call destructor?

Yes, delete[] guarantees destructors are called on every object.

Why is my destructor not being called?

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.

In what way destructor is different from delete operator?

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.

Is destructor is defined using delete keyword?

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.

Does the animal destructor call the deleting destructor?

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.

Why are my destructors not being called?

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.

What is deleting destructor in C++?

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:

Why do some classes need two Destructors in Java?

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.


3 Answers

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.

like image 23
Sander De Dycker Avatar answered Oct 28 '22 19:10

Sander De Dycker


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.

like image 75
walnut Avatar answered Oct 28 '22 21:10

walnut


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.

like image 43
Mario The Spoon Avatar answered Oct 28 '22 19:10

Mario The Spoon