[c++] Is calling destructor manually always a sign of bad design?

I was thinking: they say if you're calling destructor manually - you're doing something wrong. But is it always the case? Are there any counter-examples? Situations where it is neccessary to call it manually or where it is hard/impossible/impractical to avoid it?

This question is related to c++ coding-style destructor

The answer is


I found 3 occasions where I needed to do this:

  • allocating/deallocating objects in memory created by memory-mapped-io or shared memory
  • when implementing a given C interface using C++ (yes this still happens today unfortunately (because I don't have enough clout to change it))
  • when implementing allocator classes

I have another situation where I think it is perfectly reasonable to call the destructor.

When writing a "Reset" type of method to restore an object to its initial state, it is perfectly reasonable to call the Destructor to delete the old data that is being reset.

class Widget
{
private: 
    char* pDataText { NULL  }; 
    int   idNumber  { 0     };

public:
    void Setup() { pDataText = new char[100]; }
    ~Widget()    { delete pDataText;          }

    void Reset()
    {
        Widget blankWidget;
        this->~Widget();     // Manually delete the current object using the dtor
        *this = blankObject; // Copy a blank object to the this-object.
    }
};

What about this?
Destructor is not called if an exception is thrown from the constructor, so I have to call it manually to destroy handles that have been created in the constructor before the exception.

class MyClass {
  HANDLE h1,h2;
  public:
  MyClass() {
    // handles have to be created first
    h1=SomeAPIToCreateA();
    h2=SomeAPIToCreateB();        
    try {
      ...
      if(error) {
        throw MyException();
      }
    }
    catch(...) {
      this->~MyClass();
      throw;
    }
  }
  ~MyClass() {
    SomeAPIToDestroyA(h1);
    SomeAPIToDestroyB(h2);
  }
};

Found another example where you would have to call destructor(s) manually. Suppose you have implemented a variant-like class that holds one of several types of data:

struct Variant {
    union {
        std::string str;
        int num;
        bool b;
    };
    enum Type { Str, Int, Bool } type;
};

If the Variant instance was holding a std::string, and now you're assigning a different type to the union, you must destruct the std::string first. The compiler will not do that automatically.


Any time you need to separate allocation from initialization, you'll need placement new and explicit calling of the destructor manually. Today, it's rarely necessary, since we have the standard containers, but if you have to implement some new sort of container, you'll need it.


As quoted by the FAQ, you should call the destructor explicitly when using placement new.

This is about the only time you ever explicitly call a destructor.

I agree though that this is seldom needed.


I have never come across a situation where one needs to call a destructor manually. I seem to remember even Stroustrup claims it is bad practice.


No, Depends on the situation, sometimes it is legitimate and good design.

To understand why and when you need to call destructors explicitly, let's look at what happening with "new" and "delete".

To created an object dynamically, T* t = new T; under the hood: 1. sizeof(T) memory is allocated. 2. T's constructor is called to initialize the allocated memory. The operator new does two things: allocation and initialization.

To destroy the object delete t; under the hood: 1. T's destructor is called. 2. memory allocated for that object is released. the operator delete also does two things: destruction and deallocation.

One writes the constructor to do initialization, and destructor to do destruction. When you explicitly call the destructor, only the destruction is done, but not the deallocation.

A legitimate use of explicitly calling destructor, therefore, could be, "I only want to destruct the object, but I don't (or can't) release the memory allocation (yet)."

A common example of this, is pre-allocating memory for a pool of certain objects which otherwise have to be allocated dynamically.

When creating a new object, you get the chunk of memory from the pre-allocated pool and do a "placement new". After done with the object, you may want to explicitly call the destructor to finish the cleanup work, if any. But you won't actually deallocate the memory, as the operator delete would have done. Instead, you return the chunk to the pool for reuse.


There are cases when they are necessary:

In code I work on I use explicit destructor call in allocators, I have implementation of simple allocator that uses placement new to return memory blocks to stl containers. In destroy I have:

  void destroy (pointer p) {
    // destroy objects by calling their destructor
    p->~T();
  }

while in construct:

  void construct (pointer p, const T& value) {
    // initialize memory with placement new
    #undef new
    ::new((PVOID)p) T(value);
  }

there is also allocation being done in allocate() and memory deallocation in deallocate(), using platform specific alloc and dealloc mechanisms. This allocator was used to bypass doug lea malloc and use directly for example LocalAlloc on windows.


All answers describe specific cases, but there is a general answer:

You call the dtor explicitly every time you need to just destroy the object (in C++ sense) without releasing the memory the object resides in.

This typically happens in all the situation where memory allocation / deallocation is managed independently from object construction / destruction. In those cases construction happens via placement new upon an existent chunk of memory, and destruction happens via explicit dtor call.

Here is the raw example:

{
  char buffer[sizeof(MyClass)];

  {
     MyClass* p = new(buffer)MyClass;
     p->dosomething();
     p->~MyClass();
  }
  {
     MyClass* p = new(buffer)MyClass;
     p->dosomething();
     p->~MyClass();
  }

}

Another notable example is the default std::allocator when used by std::vector: elements are constructed in vector during push_back, but the memory is allocated in chunks, so it pre-exist the element contruction. And hence, vector::erase must destroy the elements, but not necessarily it deallocates the memory (especially if new push_back have to happen soon...).

It is "bad design" in strict OOP sense (you should manage objects, not memory: the fact objects require memory is an "incident"), it is "good design" in "low level programming", or in cases where memory is not taken from the "free store" the default operator new buys in.

It is bad design if it happens randomly around the code, it is good design if it happens locally to classes specifically designed for that purpose.


No, you shouldn't call it explicitly because it would be called twice. Once for the manual call and another time when the scope in which the object is declared ends.

Eg.

{
  Class c;
  c.~Class();
}

If you really need to perform the same operations you should have a separate method.

There is a specific situation in which you may want to call a destructor on a dynamically allocated object with a placement new but it doesn't sound something you will ever need.


Examples related to c++

Method Call Chaining; returning a pointer vs a reference? How can I tell if an algorithm is efficient? Difference between opening a file in binary vs text How can compare-and-swap be used for a wait-free mutual exclusion for any shared data structure? Install Qt on Ubuntu #include errors detected in vscode Cannot open include file: 'stdio.h' - Visual Studio Community 2017 - C++ Error How to fix the error "Windows SDK version 8.1" was not found? Visual Studio 2017 errors on standard headers How do I check if a Key is pressed on C++

Examples related to coding-style

Method Call Chaining; returning a pointer vs a reference? 80-characters / right margin line in Sublime Text 3 Cannot find reference 'xxx' in __init__.py - Python / Pycharm How to stick <footer> element at the bottom of the page (HTML5 and CSS3)? Simple way to create matrix of random numbers Is calling destructor manually always a sign of bad design? Count all values in a matrix greater than a value Iterate through a C++ Vector using a 'for' loop Which comment style should I use in batch files? Dictionaries and default values

Examples related to destructor

Is calling destructor manually always a sign of bad design? When is a C++ destructor called? How to destroy an object? How to force deletion of a python object? Creating an object: with or without `new` When should I create a destructor? How do I correctly clean up a Python object? Does delete on a pointer to a subclass call the base class destructor? Do I need to explicitly call the base virtual destructor? Dynamically allocating an array of objects