101

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?

Violet Giraffe
  • 32,368
  • 48
  • 194
  • 335
  • How are you going to deallocate the object after calling the dtor, without calling it again? – ssube Jan 06 '13 at 21:36
  • 2
    @peachykeen: you would call placement `new` to initialize a new object in place of the old. Generally not a good idea, but it isn't unheard of. – D.Shawley Jan 06 '13 at 21:38
  • 18
    Look at "rules" that contain the words "always" and "never" that don't come directly from specifications with suspect: in the most of the cases who is teaching them wants to hide you things you should know but he doesn't know how to teach. Just like an adult answering to a child to a question about sex. – Emilio Garavaglia Jan 06 '13 at 22:24
  • I think it's fine in case of manipulating with constructing objects with placement technic http://www.stroustrup.com/bs_faq2.html#placement-delete (but it's rather low level thing and is using only when you optimize your software even in such level) – Konstantin Burlachenko Aug 21 '18 at 14:40

12 Answers12

133

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.

Emilio Garavaglia
  • 20,229
  • 2
  • 46
  • 63
109

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.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • 33
    Instead of saying "using an overloaded form of `operator new`", the correct phrase is "using `placement new`". – Remy Lebeau Jan 06 '13 at 22:47
  • 5
    @RemyLebeau: Well, I wanted to clarify that I'm not only talking only of `operator new(std::size_t, void*)` (and the array variation) but rather about all overloaded version of `operator new()`. – Dietmar Kühl Jan 06 '13 at 22:56
  • What about when you want to copy an object to do an operation in it without altering it while the operation is computing? `temp = Class(object); temp.operation(); object.~Class(); object = Class(temp); temp.~Class();` – Jean-Luc Nacif Coelho Feb 23 '17 at 02:20
  • `yes, using an explicit destructor followed by a copy constructor call in the assignment operator is a bad design and likely to be wrong`. Why do you say this? I would think that if the destructor is trivial, or close to trivial, it has minimal overhead and increases the use of the DRY principle. If used in such cases with a move `operator=()`, it may even be better than using swap. YMMV. – Adrian Apr 03 '18 at 15:07
  • 1
    @Adrian: calling the destructor and recreating the object very easily changes the type of the object: it will recreate an object with the static type of the assignment but the dynamic type may be different. That is actually an issue when the class has `virtual` functions (the `virtual` functions won’t be recreated) and otherwise the object is just partially [re-]constructed. – Dietmar Kühl Apr 03 '18 at 21:19
  • @DietmarKühl, having an assignment operator, for an inherited class object is problematic already, so I would say that your argument is moot. – Adrian Apr 04 '18 at 05:36
  • This may not actually be correct if you have overloaded the user-defined placement allocation functions since you can override the delete operator to function properly with these functions. You might do this in a large project to support various custom allocators. – Dyllon Gagnier Apr 12 '23 at 19:25
14

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.

13

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.

Jack
  • 131,802
  • 30
  • 241
  • 343
9

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.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
6

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.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
3

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.

marcinj
  • 48,511
  • 9
  • 79
  • 100
3

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.

Violet Giraffe
  • 32,368
  • 48
  • 194
  • 335
2

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);
  }
};
CITBL
  • 1,587
  • 3
  • 21
  • 36
  • 2
    This seems questionable: when your constructor trows, you don't know (or may not know) which parts of the object had been constructed and which hadn't. So you don't know for which sub-objects to call destructors, for instance. Or which of the resources allocated by the constructor to deallocate. – Violet Giraffe Jun 30 '18 at 08:13
  • @VioletGiraffe if the sub-objects are constructed on stack, i.e. not with "new", they will be destroyed automatically. Otherwise you can check if they are NULL before destroying them in the destructor. Same with the resources – CITBL Jul 01 '18 at 16:50
  • 1
    The way you wrote the `ctor` here is wrong, exactly for the reason you provided yourself: if resource allocation fails, there's a problem with cleanup. A 'ctor' should not call `this->~dtor()`. `dtor` should be called on **constructed** objects, and in this case the object is not yet constructed. Whatever happens, the `ctor` should handle the cleanup. Inside the `ctor` code, you should use utils like `std::unique_ptr` to handle the automatic cleanup for you in cases when something throws. Changing `HANDLE h1, h2` fields in the class to support automatic cleanup could be a nice idea as well. – quetzalcoatl Jul 15 '19 at 16:16
  • This means, that the ctor should look like: `MyClass(){ cleanupGuard1 tmp_h1(&SomeAPIToDestroyA) = SomeAPIToCreateA(); cleanupGuard2 tmp_h2(&SomeAPIToDestroyB) = SomeAPIToCreateB(); if(error) { throw MyException(); } this->h1 = tmp_h1.release(); this->h2 = tmp_h2.release(); }` and **that's it**. No risky manual cleanup, no storing handles in partially-constructed object until everything is safe comes as a bonus. If you change `HANDLE h1,h2` in the class to `cleanupGuard h1;` etc, then you may even not need the `dtor` at all. – quetzalcoatl Jul 15 '19 at 16:19
  • Implementation of `cleanupGuard1` and `cleanupGuard2` depends on what does a relevant `xxxToCreate` return and what parameters does a relevant `xxxxToDestroy` take. If they are simple, you might even not need to write anything, as it often turns out that `std::unique_ptr` (or similar one) can do the trick for you in both cases. – quetzalcoatl Jul 15 '19 at 16:22
1

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
0

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.

Lieuwe
  • 1,734
  • 2
  • 27
  • 41
  • 1
    You are correct. But I have used a placement new. I was able to add the cleanup functionality in a method other then the destructor. The destructor is there so it can "automatically" be called when one does delete, when you manually want to destruct but not deallocate you could simply write an "onDestruct" couldn't you? I would be interested to hear if there are examples where an object would have to do its destruction in the destructor because sometimes you would need to delete and other times you would only want to destruct and not deallocate.. – Lieuwe Jan 07 '13 at 09:09
  • And even in that case you could call onDestruct() from within the destructor - so I still don't see a case for manually calling the destructor. – Lieuwe Jan 07 '13 at 09:26
  • 5
    @JimBalter: creator of `C+` ☺ – Mark K Cowan Sep 14 '16 at 14:13
  • @MarkKCowan: what is C+ ? It should be C++ – Destructor Jan 09 '17 at 06:11
-4

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.
    }
};
abelenky
  • 63,815
  • 23
  • 109
  • 159
  • 2
    Wouldn't it look cleaner if you declared a special `cleanup()` method to be called in this case *and* in the destructor? – Violet Giraffe May 25 '16 at 14:19
  • A "special" method that is only called in two cases? Sure... that sounds totally correct (/sarcasm). Methods should be generalized and able to be called anywhere. When you want to delete an object, there is nothing wrong with calling its destructor. – abelenky May 25 '16 at 14:44
  • 5
    You must not call the destructor explicitly in this situation. You'd have to implement an assignment operator, anyway. – Rémi Jul 13 '17 at 13:38