In a declaration of a destructor, the declarator is a function declarator of the form
ptr-declarator ( parameter-declaration-clause ) noexcept-specifieropt attribute-specifier-seqopt
where the ptr-declarator consists solely of an id-expression, an optional attribute-specifier-seq, and optional surrounding parentheses, and the id-expression has one of the following forms:
in a member-declaration that belongs to the member-specification of a class but is not a friend declaration, the id-expression is ~class-name and the class-name is the injected-class-name of the immediately-enclosing class;
in a member-declaration that belongs to the member-specification of a class template but is not a friend declaration, the id-expression is ~class-name and the class-name names the current instantiation of the immediately-enclosing class template; or
in a declaration at namespace scope or in a friend declaration, the id-expression is nested-name-specifier ~class-name and the class-name names the same class as the nested-name-specifier.
The class-name shall not be a typedef-name. A destructor shall take no arguments ([dcl.fct]). Each decl-specifier of the decl-specifier-seq of a destructor declaration (if any) shall be friend, inline, or virtual.
A destructor is used to destroy objects of its class type. The address of a destructor shall not be taken. A destructor can be invoked for a const, volatile or const volatile object. const and volatile semantics ([dcl.type.cv]) are not applied on an object under destruction. They stop being in effect when the destructor for the most derived object starts.
[ Note: A declaration of a destructor that does not have a noexcept-specifier has the same exception specification as if had been implicitly declared ([except.spec]). — end note ]
If a class has no user-declared destructor, a destructor is implicitly declared as defaulted. An implicitly-declared destructor is an inline public member of its class.
A defaulted destructor for a class X is defined as deleted if:
X is a union-like class that has a variant member with a non-trivial destructor,
any potentially constructed subobject has class type M (or array thereof) and M has a deleted destructor or a destructor that is inaccessible from the defaulted destructor,
or, for a virtual destructor, lookup of the non-array deallocation function results in an ambiguity or in a function that is deleted or inaccessible from the defaulted destructor.
A destructor is trivial if it is not user-provided and if:
the destructor is not virtual,
all of the direct base classes of its class have trivial destructors, and
for all of the non-static data members of its class that are of class type (or array thereof), each such class has a trivial destructor.
Otherwise, the destructor is non-trivial.
A destructor that is defaulted and not defined as deleted is implicitly defined when it is odr-used or when it is explicitly defaulted after its first declaration.
Before the defaulted destructor for a class is implicitly defined, all the non-user-provided destructors for its base classes and its non-static data members shall have been implicitly defined.
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 non-virtual direct base classes and, if X is the type of the most derived class ([class.base.init]), its destructor calls the destructors for X's virtual base classes. All destructors are called as if they were referenced with a qualified name, that is, ignoring any possible virtual overriding destructors in more derived classes. Bases and members are destroyed in the reverse order of the completion of their constructor (see [class.base.init]). A return statement in a destructor might not directly return to the caller; before transferring control to the caller, the destructors for the members and bases are called. Destructors for elements of an array are called in reverse order of their construction (see [class.init]).
A destructor can be declared virtual or pure virtual; if any objects of that class or any derived class are created in the program, the destructor shall be defined. If a class has a base class with a virtual destructor, its destructor (whether user- or implicitly-declared) is virtual.
[ Note: Some language constructs have special semantics when used during destruction; see [class.cdtor]. — end note ]
A destructor is invoked implicitly
for a constructed object with static storage duration at program termination,
for a constructed object with thread storage duration at thread exit,
for a constructed object with automatic storage duration when the block in which an object is created exits ([stmt.dcl]),
for a constructed temporary object when its lifetime ends ([conv.rval], [class.temporary]).
In each case, the context of the invocation is the context of the construction of the object. A destructor is also invoked implicitly through use of a delete-expression for a constructed object allocated by a new-expression; the context of the invocation is the delete-expression. [ Note: An array of class type contains several subobjects for each of which the destructor is invoked. — end note ] A destructor can also be invoked explicitly. A destructor is potentially invoked if it is invoked or as specified in [expr.new], [class.base.init], and [except.throw]. A program is ill-formed if a destructor that is potentially invoked is deleted or not accessible from the context of the invocation.
At the point of definition of a virtual destructor (including an implicit definition), the non-array deallocation function is determined as if for the expression delete this appearing in a non-virtual destructor of the destructor's class (see [expr.delete]). If the lookup fails or if the deallocation function has a deleted definition, the program is ill-formed. [ Note: This assures that a deallocation function corresponding to the dynamic type of an object is available for the delete-expression ([class.free]). — end note ]
In an explicit destructor call, the destructor is specified by a ~ followed by a type-name or decltype-specifier that denotes the destructor's class type. The invocation of a destructor is subject to the usual rules for member functions; that is, if the object is not of the destructor's class type and not of a class derived from the destructor's class type (including when the destructor is invoked via a null pointer value), the program has undefined behavior. [ Note: Invoking delete on a null pointer does not call the destructor; see [expr.delete]. — end note ] [ Example:
struct B { virtual ~B() { } }; struct D : B { ~D() { } }; D D_object; typedef B B_alias; B* B_ptr = &D_object; void f() { D_object.B::~B(); // calls B's destructor B_ptr->~B(); // calls D's destructor B_ptr->~B_alias(); // calls D's destructor B_ptr->B_alias::~B(); // calls B's destructor B_ptr->B_alias::~B_alias(); // calls B's destructor }
— end example ] [ Note: An explicit destructor call must always be written using a member access operator or a qualified-id ([expr.prim]); in particular, the unary-expression ~X() in a member function is not an explicit destructor call ([expr.unary.op]). — end note ]
[ 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. For example,
void* operator new(std::size_t, void* p) { return p; } struct X { X(int); ~X(); }; void f(X* p); void g() { // rare, specialized use: char* buf = new char[sizeof(X)]; X* p = new(buf) X(222); // use buf[] and initialize f(p); p->X::~X(); // cleanup }
— end note ]
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. [ 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 ]
[ Note: The notation for explicit call of a destructor can be used for any scalar type name ([expr.pseudo]). Allowing this makes it possible to write code without having to know if a destructor exists for a given type. For example:
typedef int I; I* p; p->I::~I();
— end note ]