15 Special member functions [special]

15.4 Destructors [class.dtor]

In a declaration of a destructor, the declarator is a function declarator ([dcl.fct]) of the form
ptr-declarator ( parameter-declaration-clause ) noexcept-specifier attribute-specifier-seq
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:
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 ([intro.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 ([dcl.fct.def]).
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 ([basic.def.odr]) 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 ([stmt.return]) 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 ([class.virtual]) or pure virtual ([class.abstract]); 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
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 ([class.copy])), 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 ([dcl.fct.def]), 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 ([class.mfct]); 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 ([expr.ref]) 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 ([basic.life]).
[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
]