HOWTO · C++

How to Call a Destructor Explicitly in C++

Learn the explicit destructor-call syntax in C++, when cleanup happens automatically, and why an object must not be used after its lifetime ends.

On this page

In C++, write obj.~ClassName() to invoke an object’s destructor explicitly. This ends that object’s lifetime, so do not use the object again unless a new object is created in its storage. Manual calls are uncommon; ordinary scope-based cleanup or an ownership type such as std::unique_ptr is usually safer.

Use the obj.~ClassName() Notation to Explicitly Call a Destructor Function

A destructor releases resources owned by an object as its lifetime ends. The compiler calls it automatically when an automatic object leaves its scope. For a pointer returned by new, delete calls the destructor and then releases the object’s storage. These normal paths should not be combined with an extra manual call: explicitly destroying an automatic object and then letting it leave scope attempts to destroy an object whose lifetime has already ended. That is undefined behavior; it does not reliably print two messages. Likewise, manually destroying an object created with new and later applying delete to the same pointer is not a valid cleanup sequence.

An explicit call is useful in carefully managed low-level code, such as when an object occupies caller-provided storage and the program will construct another object there. The following example uses placement new to begin an object’s lifetime in a byte buffer, invokes its destructor, then constructs a replacement at the same address. The final explicit call ends the replacement’s lifetime; the byte buffer itself does not automatically destroy objects stored inside it.

The call uses the object’s expression, followed by .~ and the class name: for an object named obj of class Widget, the form is obj.~Widget(). The destructor has no return value and takes no arguments. Its access rules still apply, so a private destructor cannot be called from code that is not allowed to access it. Calling the function performs the destructor’s cleanup work, but it does not release storage obtained from an allocator or new; storage management is a separate responsibility.

In the example, alignas(Widget) gives the byte buffer the alignment required for a Widget, and its size is large enough for one instance. Placement new constructs the object in that existing buffer rather than requesting a separate allocation. The first explicit call ends the first instance’s lifetime before the replacement is constructed. The replacement is then destroyed explicitly as well. Because the storage is a local byte array, its scope ending only ends the array’s lifetime; it does not call a destructor for a Widget that was placed there. This pattern requires the programmer to track which object is alive in the storage and to ensure each constructed object is destroyed exactly once.

For ordinary application code, a local variable is simpler: its destructor runs when control leaves the block, including when a function returns. A std::unique_ptr<Widget> is a good fit when ownership must be dynamic, because the owner ties object destruction to its own lifetime. When an object is part of a container or another class, let that owner manage destruction rather than calling a member object’s destructor by hand. These approaches make cleanup visible in the program structure and avoid using an object after its lifetime has ended.

Run this example:

#include <iostream>
#include <new>
#include <string>
#include <utility>

class Widget {
 public:
  explicit Widget(std::string name) : name_(std::move(name)) {
    std::cout << "created " << name_ << '\n';
  }

  ~Widget() { std::cout << "destroyed " << name_ << '\n'; }

 private:
  std::string name_;
};

int main() {
  alignas(Widget) unsigned char storage[sizeof(Widget)];
  Widget* obj = ::new (static_cast<void*>(storage)) Widget("first");
  obj->~Widget();
  obj = ::new (static_cast<void*>(storage)) Widget("second");
  obj->~Widget();
}
created first
destroyed first
created second
destroyed second

In code that does not manage storage and lifetimes directly, let a local object’s scope end naturally. For dynamically allocated ownership, prefer std::unique_ptr; its destructor uses delete when the owner leaves scope. Do not access a pointer after its object has been destroyed, and do not manually invoke a destructor merely to release a member resource: use an appropriate standard-library owner or resource wrapper instead.