Does Calling a Destructor Explicitly Destroy an Object Completely

Does calling a destructor explicitly destroy an object completely?

The answer is... nearly always.

If your object has a non-virtual destructor, and is then sub-classed to add child elements that need freeing... then calling the destructor on the object base class will not free the child elements. This is why you should always declare destructors virtual.

We had an interesting case where two shared libraries referenced an object. We changed the definition to add child objects which needed freeing. We recompiled the first shared library which contained the object definition.

HOWEVER, the second shared library was not recompiled. This means that it did not know of the newly added virtual object definition. Delete's invoked from the second shared library simply called free, and did not invoke the virtual destructor chain. Result was a nasty memory leak.

Automatic destruction of object even after calling destructor explicitly

You've introduced undefined behavior.

Per the standard:

§ 12.4 Destructors

(11) A destructor is invoked implicitly

(11.3) — for a constructed object with automatic storage duration (3.7.3) when the block in which an object is
created exits
(6.7),

and

15 Once a destructor is invoked for an object, the object no longer exists; the behavior is undefined if the
destructor is invoked for an object whose lifetime has ended (3.8). [ Example: if the destructor for an
automatic object is explicitly invoked, and the block is subsequently left in a manner that would ordinarily
invoke implicit destruction of the object, the behavior is undefined. —end example ]

You explicitly call the destructor or by calling t.~Test(), it is then implicitly invoked when the object leaves scope. This is undefined.

The standard provides this note as well:

14 [ Note: explicit calls of destructors are rarely needed. One use of such calls is for objects placed at specific
addresses using a placement new-expression. Such use of explicit placement and destruction of objects can
be necessary to cope with dedicated hardware resources and for writing memory management facilities.

Does Explicitly Calling Child Destructors also call Parent Destructors

Yes, that's guaranteed. C++14 [class.dtor]/8:

After executing the body of the destructor and destroying any automatic objects allocated within the body, a destructor for class X calls the destructors for X’s direct non-variant non-static data members, the destructors for X’s direct base classes and, if X is the type of the most derived class (12.6.2), its destructor calls the destructors for X’s virtual base classes.

Specifically, for any type T you can always do this:

void * addr = ::operator new(sizeof T);
T * p = ::new (addr) T(/* ctor args */);
p->~T(); // !
::operator delete(addr);

Or, if you didn't want to invoke an allocator:

{
std::aligned_storage_t<sizeof(T), alignof(T)> storage;
T * p = ::new (static_cast<void *>(std::addressof(storage))) T(/* args */);
p->~T();
}

At which point of destructor call the object ceases to exist?

At which point of destructor call the object ceases to exist?

The lifetime of the object is ended by the call to its destructor. Within the destructor body, the sub-objects are still alive and member functions may be called. After the destructor body, the sub objects are destroyed.

if it's legal to call an object's function inside its destructor or not

It is legal.

Note however that calling a virtual function works differently than one might expect.

Manual call of destructor

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.

Is calling destructor manually always a sign of bad design?

Calling the destructor manually is required if the object was constructed using an overloaded form of operator new(), except when using the "std::nothrow" overloads:

T* t0 = new(std::nothrow) T();
delete t0; // OK: std::nothrow overload

void* buffer = malloc(sizeof(T));
T* t1 = new(buffer) T();
t1->~T(); // required: delete t1 would be wrong
free(buffer);

Outside managing memory on a rather low level as above calling destructors explicitly, however, is a sign of bad design. Probably, it is actually not just bad design but outright wrong (yes, using an explicit destructor followed by a copy constructor call in the assignment operator is a bad design and likely to be wrong).

With C++ 2011 there is another reason to use explicit destructor calls: When using generalized unions, it is necessary to explicitly destroy the current object and create a new object using placement new when changing the type of the represented object. Also, when the union is destroyed, it is necessary to explicitly call the destructor of the current object if it requires destruction.

Pseudo-destructor call does not destroy an object

But it is not true, why?

§5.2.4/1:

The only effect is the evaluation of the postfix-expression before the dot or arrow.

Where the postfix-expression is the expression of the object for which the call takes place. Thus a pseudo destructor call, as a call to a trivial destructor, does not end the lifetime of the object it is applied to. For instance,

int i = 0;
(i += 5).~decltype(i)();
std::cout << i;

You can't actually call a destructor for scalars, because they don't have one (see [class.dtor]). The statement is solely allowed for template code in which you call the destructor of an object whose type you don't know - it removes the necessity of writing a specialization for scalar types.


It was noted in the comments that [expr.pseudo] does imply the existence of a destructor for scalars by

The use of a pseudo-destructor-name after a dot . or arrow -> operator
represents the destructor for the non-class type named by type-name.

However, this is inconsistent with other parts of the standard, e.g. §12, which calls a destructor a special member function and mentions that

A destructor is used to destroy objects of its class type.

It appears to be an imprecision created in C++98 days.



Related Topics



Leave a reply



Submit