Manual call of destructor

Viewed 1016
#include <iostream> 
using namespace std; 
namespace GB
{
    class Test 
    { 
    public: 
        Test()  { cout << "Constructor is executed\n"; } 
        ~Test() { 
            cout << i << " " << "Destructor is executed\n";  
            this->i = 7;
        } 
        int i = -1;
    }; 
}
  
int main() 
{ 
    // Test();  // Explicit call to constructor 
    GB::Test t;    // local object 
    t.i = 6;
    t.~Test(); // Explicit call to destructor 
    return 0; 
}

Output

Constructor is executed
6 Destructor is executed
6 Destructor is executed

My questions are:
1)Why destructor is called twice.
2)In first call of destructor memeber value is changed from 6 to 7 , still in second call it comes as 6.
3)Can we stop second call of destructor (I want to keep only manually call of destructor).

3 Answers

Why destructor is called twice.

The first call is from the line i.~Test();.
The second call is the automatic call to the destructor when the variable i gets out of scope (before returning from main).

In first call of destructor memeber value is changed from 6 to 7 , still in second call it comes as 6.

That's caused by undefined behavior. When an object's destructor gets called twice, you should expect undefined behavior. Don't try to make logical sense when a program enters undefined behavior territory.

Can we stop second call of destructor (I want to keep only manually call of destructor).

You can't disable the call to the destructor of an automatic variable when variable goes out of scope.

If you want to control when the destructor is called, create an object using dynamic memory (by calling new Test) and destroy the object by calling delete.

GB::Test* t = new GB::Test(); // Calls the constructor
t->i = 6;
delete t;                     // Calls the destructor

Even in this case, calling the destructor explicitly is almost always wrong.

t->~Test();  // Almost always wrong. Don't do it.

Please note that if you want to create objects using dynamic memory, it will be better to use smart pointers. E.g.

auto t = std::make_unique<GB::Test>();  // Calls the constructor
t->i = 6;
t.reset();                              // Calls the destructor

If t.reset(); is left out, the dynamically allocated object's destructor will be called and the memory will be deallocated when t gets out of scope. t.reset(); allows you to control when the underlying object gets deleted.

1)Why destructor is called twice.

Because it's C++ and not Pascal, and it's the language's job to destruct objects when they die (i.e. when their lifetime ends). Object lifetimes are an integral part of semantics of any language, and they often are what differentiates languages, so you can't just assume that C++ will behave like some other language you are thinking of. Since the language destructs the object when its lifetime ends at the end of the scope ({ block }), the second and redundant call is the earlier one that you manually wrote. The whole point of C++ is, in a way, to take care of this for you: if you think that you need to manually destruct things, you're not thinking C++, usually.

2)In first call of destructor memeber value is changed from 6 to 7 , still in second call it comes as 6.

The object ceases to exist after destruction, so changes to object's state within the destructor will be lost. The second invocation of the destructor is undefined behavior, so it can do "anything". The compiler likely optimized away the this->i = 7 assignment in the destructor, since that assignment has no side effects that are meant to be observable outside of it: you wrote dead code, don't be surprised it's treated like dead code.

3)Can we stop second call of destructor (I want to keep only manually call of destructor).

No. C++ is not Pascal. The whole point is that destructors run automatically. That's the major semantic difference between C++ and many other object-oriented languages: C++ destructors are also nothing like finalizers in Java or .Net (C#). If you want to destroy the object earlier, limit the scope of the object appropriately. Forget about manually calling destructors.

In other words: stop thinking of a destructor as if it was a function you call. Instead, think of how to manage the object's lifetime so that it gets destroyed as you deem convenient.

I'd like to add to the other excellent answers.

It is possible to explicitly call a constructor and destructor of an object without using the heap by using a union. Here is an example for that:

namespace GB
{
    class Test
    {
    public:
        Test()  { cout << "Constructor is executed\n"; }
        ~Test() {
            cout << i << " " << "Destructor is executed\n";
            this->i = 7;
        }
        int i = -1;
    };
    union OptTest
    {
        OptTest() : test() {}  // if there is no need to control the construction
        ~OptTest() {}
        Test test;
        char none;
    };
}
int main()
{
    // EDIT: The following comment appears in the original question,
    //       but it is misleading. Test() will create a temporary object,
    //       which will be immediately destroyed. Use placement-new to
    //       explicitly call a constructor instead.

    // Test();  // Explicit call to constructor
    GB::OptTest ot;    // local object
    ot.test.i = 6;
    ot.test.~Test(); // Explicit call to destructor
    return 0;
}

Whether or not this is a good idea depends on the use case. For example, if you want to implement something like std::optional<Test> then using union is a good way to control the destruction of Test.

NOTE: The none field is not strictly required, but in my opinion it better conveys the fact that the union may be in one of two states (with a Test object and without - after the destructor is called).

Related