Search code examples
c++destructorundefined-behaviorlifetimeexplicit-destructor-call

Explicit call to destructor


I stumbled upon the following code snippet:

#include <iostream>
#include <string>
using namespace std;
class First
{
    string *s;
    public:
    First() { s = new string("Text");}
    ~First() { delete s;}
    void Print(){ cout<<*s;}
};

int main()
{
    First FirstObject;
    FirstObject.Print();
    FirstObject.~First();
}

The text said that this snippet should cause a runtime error. Now, I wasn't really sure about that, so I tried to compile and run it. It worked. The weird thing is, despite the simplicity of the data involved, the program stuttered after printing "Text" and only after one second it completed.

I added a string to be printed to the destructor as I was unsure if it was legal to explicitly call a destructor like that. The program printed twice the string. So my guess was that the destructor is called twice as the normal program termination is unaware of the explicit call and tries to destroy the object again.

A simple search confirmed that explicitly calling a destructor on an automated object is dangerous, as the second call (when the object goes out of scope) has undefined behaviour. So I was lucky with my compiler (VS 2017) or this specific program.

Is the text simply wrong about the runtime error? Or is it really common to have runtime error? Or maybe my compiler implemented some kind of warding mechanism against this kind of things?


Solution

  • Its has been a while since this question, but I think I can contribute a bit more.

    First, when an object goes at of scope, its destructor is invoked. Here you are explicitly calling the destructor once, so then it is invoked a second time.

    On the second time, it deletes again the internal s pointer, which was already freed. That corrupts the internal memory structs, so it may crash now or it may crash later. You don't know.

    It is always good practice to really check and clean data before releasing it. I would have written the destructor like this :

    ~First() 
    { 
      if (s)
      {
      delete s;
      s = 0;
      }
    }
    

    Then you would have been protected from this error.

    It is not necessarily bad to manually invoke a destructor. Sometimes it is needed. Let's say you are working on an embedded device, where you don't want to use dynamic allocation constantly. At startup you could allocate a big chunk of memory, and then construct/destroy objects on it.

    Let's say you have a raw pointer, pointing to some raw buffer inside that big chunk, that you are going to use as your First object. The size of that buffer has to be at least sizeof(First).

    When you want to build the object you use placement new :

    new(p) First();

    When you want to destroy it, you invoke :

    p->~First();

    That way you never release the memory, just construct/destruct objects over the preallocated buffer.

    Just a reminder, about what new/delete do :

    new will allocate memory, and then it will invoke the constructor on it. delete will invoke the destructor and then free the memory.

    If you already have the memory, you can just invoke constructor and then destructor, without allocating or freeing.

    Being said that, your code would have worked if you had built it again :

    int main()
    {
        First FirstObject;
        FirstObject.Print();
        FirstObject.~First();
        new(&FirstObject) First(); // constructing the object a second time
    }