Destructors
A destructor is a special member function that is called when the lifetime of an object
A destructor cannot be a coroutine. |
(since C++20) |
Syntax
Destructors(until C++20) Prospective destructors(since C++20) are declared using member function declarators
class-name-with-tilde
(
parameter-list (optional)
)
except (optional)
attr (optional)
|
|||||||||
class-name-with-tilde | - | an identifier expression, possibly followed by a list of attributes, and (since C++11) | ||||||
parameter-list | - | parameter list | ||||||
except | - |
|
||||||
attr | - | (since C++11) a list of attributes |
The only specifiers allowed in the declaration specifiers of aprospective(since C++20) destructor declaration areconstexpr
,(since C++11)
friend
, inline
and virtual
The identifier expression of class-name-with-tilde must have one of the following forms:
- In a member declaration that belongs to the member specification of a class or class template, but is not a friend declaration
-
- For classes, the identifier expression is ~ followed by the injected-class-name
- For class templates, the identifier expression is ~ followed by a class name that names the current instantiation (until C++20) the injected-class-name(since C++20)
- Otherwise, the identifier expression is a qualified identifier whose terminal unqualified identifier is ~
Explanation
The destructor is implicitly invoked whenever an object's lifetime ends, which includes
- program termination, for objects with static storage duration
|
(since C++11) |
- end of scope, for objects with automatic storage duration and for temporaries whose life was extended by binding to a reference
- delete-expression, for objects with dynamic storage duration
- end of the full expression, for nameless temporaries
- stack unwinding, for objects with automatic storage duration when an exception escapes their block, uncaught.
The destructor can also be invoked explicitly.
Potentially-invoked destructor
A destructor of a class C
is potentially invoked in the following situations:
- It is invoked explicitly or implicitly.
- A new expression creates an array of objects of type
C
. - The result object of a return statement is of type
C
. - An array is under aggregate initialization, and its element type is
C
. - A class object is under aggregate initialization, and it has a member of type
C
whereC
is not an anonymous union type. - A potentially constructed subobject is of type
C
in a non-delegating (since C++11) - An exception object of type
C
is constructed.
If a potentially-invoked destructor isdeleted or(since C++11)
Prospective destructorA class may have one or more prospective destructors, one of which is selected as the destructor for the class. In order to determine which prospective destructor is the destructor, at the end of the definition of the class, overload resolution odr-use All prospective destructors are special member functions. If no user-declared prospective destructor is provided for class |
(since C++20) |
Implicitly-declared destructor
If no user-declaredprospective(since C++20) destructor is provided for a class type, the compiler will always declare a destructor as an inline public
As with any implicitly-declared special member function, the exception specification of the implicitly-declared destructor is non-throwing unless the destructor of any potentially-constructed base or member is potentially-throwing (since C++17) implicit definition would directly invoke a function with a different exception specification (until C++17) . In practice, implicit destructors are noexcept unless the class is "poisoned" by a base or member whose destructor is noexcept(false)
Implicitly-defined destructor
If an implicitly-declared destructor is not deleted, it is implicitly defined (that is, a function body is generated and compiled) by the compiler when it is odr-used
If this satisfies the requirements of a constexpr destructor(until C++23) constexpr function (since C++23) , the generated destructor is constexpr |
(since C++20) |
Deleted destructor
The implicitly-declared or explicitly-defaulted destructor for class T
is undefined(until C++11)
defined as deleted(since C++11)
-
T
has a potentially constructed subobject of class typeM
(or possibly multi-dimensional array thereof) such thatM
- is deleted or inaccessible from the destructor of
T
, or - in the case of the subobject being a variant member, is non-trivial.
- is deleted or inaccessible from the destructor of
- The destructor is virtual and the lookup for the deallocation function results in
- an ambiguity, or
- a function that is deleted or inaccessible from the destructor.
An explicitly-defaulted prospective destructor for |
(since C++20) |
Trivial destructor
The destructor for class T
is trivial if all of the following is true:
- The destructor is not user-provided (meaning, it is either implicitly declared, or explicitly defined as defaulted on its first declaration).
- The destructor is not virtual (that is, the base class destructor is not virtual).
- All direct base classes have trivial destructors.
- All non-static data members of class type (or array of class type) have trivial destructors.
A trivial destructor is a destructor that performs no action. Objects with trivial destructors don't require a delete-expression and may be disposed of by simply deallocating their storage. All data types compatible with the C language (POD types) are trivially destructible.
Destruction sequence
For both user-defined or implicitly-defined destructors, after executing the body of the destructor and destroying any automatic objects allocated within the body, the compiler calls the destructors for all non-static non-variant data members of the class, in reverse order of declaration, then it calls the destructors of all direct non-virtual base classes in reverse order of construction
Even when the destructor is called directly (e.g. obj.~Foo();), the return statement in ~Foo()
Virtual destructors
Deleting an object through pointer to base invokes undefined behavior unless the destructor in the base class is virtual:
class Base { public: virtual ~Base() {} }; class Derived : public Base {}; Base* b = new Derived; delete b; // safe
A common guideline is that a destructor for a base class must be either public and virtual or protected and nonvirtual.
Pure virtual destructors
A prospective(since C++20) destructor may be declared pure virtual
class AbstractBase { public: virtual ~AbstractBase() = 0; }; AbstractBase::~AbstractBase() {} class Derived : public AbstractBase {}; // AbstractBase obj; // compiler error Derived obj; // OK
Exceptions
As any other function, a destructor may terminate by throwing an exception (this usually requires it to be explicitly declared noexcept(false)) (since C++11) , however if this destructor happens to be called during stack unwinding, std::terminate
Although std::uncaught_exceptions may sometimes be used to detect stack unwinding in progress, it is generally considered bad practice to allow any destructor to terminate by throwing an exception. This functionality is nevertheless used by some libraries, such as SOCI and Galera 3
std::experimental::scope_success in Library fundamental TS v3 may have a potentially-throwing destructor
Notes
Calling a destructor directly for an ordinary object, such as a local variable, invokes undefined behavior when the destructor is called again, at the end of scope.
In generic contexts, the destructor call syntax can be used with an object of non-class type; this is known as pseudo-destructor call: see member access operator
Example
Output:
ctor a0 ctor a1 ctor a2 ctor a3 dtor a2 dtor a3 dtor a1 dtor a0
Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
CWG 193 | C++98 | whether automatic objects in a destructor are destroyed before or after the destruction of the class's base and member subobjects was unspecified |
they are destroyed before destroying those subobjects |
CWG 344 | C++98 | the declarator syntax of destructor was defective (had the same problem as CWG issue 194 and CWG issue 263 |
changed the syntax to a specialized function declarator syntax |
CWG 1241 | C++98 | static members might be destroyed right after destructor execution |
only destroy non- static members |
CWG 1353 | C++98 | the conditions where implicitly-declared destructors are undefined did not consider multi-dimensional array types |
consider these types |
CWG 1435 | C++98 | the meaning of “class name” in the declarator syntax of destructor was unclear |
changed the syntax to a specialized function declarator syntax |
CWG 2180 | C++98 | the destructor of class X called thedestructors for X 's virtual direct base classes
|
those destructors are not called |
CWG 2807 | C++20 | the declaration specifiers could contain consteval | prohibited |