390 lines
18 KiB
Markdown
390 lines
18 KiB
Markdown
[class.dtor]
|
||
|
||
# 11 Classes [[class]](./#class)
|
||
|
||
## 11.4 Class members [[class.mem]](class.mem#class.dtor)
|
||
|
||
### 11.4.7 Destructors [class.dtor]
|
||
|
||
[1](#1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/classes.tex#L2082)
|
||
|
||
A declaration whose [*declarator-id*](dcl.decl.general#nt:declarator-id "9.3.1 General [dcl.decl.general]") has an [*unqualified-id*](expr.prim.id.unqual#nt:unqualified-id "7.5.5.2 Unqualified names [expr.prim.id.unqual]") that begins with a ~ declares a [*prospective destructor*](#def:destructor,prospective "11.4.7 Destructors [class.dtor]");
|
||
its [*declarator*](dcl.decl.general#nt:declarator "9.3.1 General [dcl.decl.general]") shall be a function declarator ([[dcl.fct]](dcl.fct "9.3.4.6 Functions")) of the form
|
||
|
||
[*ptr-declarator*](dcl.decl.general#nt:ptr-declarator "9.3.1 General [dcl.decl.general]") ( [*parameter-declaration-clause*](dcl.fct#nt:parameter-declaration-clause "9.3.4.6 Functions [dcl.fct]") ) [*noexcept-specifier*](except.spec#nt:noexcept-specifier "14.5 Exception specifications [except.spec]")opt [*attribute-specifier-seq*](dcl.attr.grammar#nt:attribute-specifier-seq "9.13.1 Attribute syntax and semantics [dcl.attr.grammar]")opt
|
||
|
||
where the [*ptr-declarator*](dcl.decl.general#nt:ptr-declarator "9.3.1 General [dcl.decl.general]") consists solely of an[*id-expression*](expr.prim.id.general#nt:id-expression "7.5.5.1 General [expr.prim.id.general]"), an optional [*attribute-specifier-seq*](dcl.attr.grammar#nt:attribute-specifier-seq "9.13.1 Attribute syntax and semantics [dcl.attr.grammar]"),
|
||
and optional surrounding parentheses, and the [*id-expression*](expr.prim.id.general#nt:id-expression "7.5.5.1 General [expr.prim.id.general]") has
|
||
one of the following forms:
|
||
|
||
- [(1.1)](#1.1)
|
||
|
||
in a [*member-declaration*](class.mem.general#nt:member-declaration "11.4.1 General [class.mem.general]") that belongs to the[*member-specification*](class.mem.general#nt:member-specification "11.4.1 General [class.mem.general]") of a class or class template
|
||
but is not a friend
|
||
declaration ([[class.friend]](class.friend "11.8.4 Friends")), the [*id-expression*](expr.prim.id.general#nt:id-expression "7.5.5.1 General [expr.prim.id.general]") is~[*class-name*](class.pre#nt:class-name "11.1 Preamble [class.pre]") and the [*class-name*](class.pre#nt:class-name "11.1 Preamble [class.pre]") is the
|
||
injected-class-name ([[class.pre]](class.pre "11.1 Preamble")) of the immediately-enclosing entity or
|
||
|
||
- [(1.2)](#1.2)
|
||
|
||
otherwise, the[*id-expression*](expr.prim.id.general#nt:id-expression "7.5.5.1 General [expr.prim.id.general]") is [*nested-name-specifier*](expr.prim.id.qual#nt:nested-name-specifier "7.5.5.3 Qualified names [expr.prim.id.qual]")~[*class-name*](class.pre#nt:class-name "11.1 Preamble [class.pre]") and the [*class-name*](class.pre#nt:class-name "11.1 Preamble [class.pre]") is the injected-class-name of the
|
||
class nominated by the [*nested-name-specifier*](expr.prim.id.qual#nt:nested-name-specifier "7.5.5.3 Qualified names [expr.prim.id.qual]")[.](#1.sentence-1)
|
||
|
||
A prospective destructor shall take no arguments ([[dcl.fct]](dcl.fct "9.3.4.6 Functions"))[.](#1.sentence-2)
|
||
|
||
Each [*decl-specifier*](dcl.spec.general#nt:decl-specifier "9.2.1 General [dcl.spec.general]") of the [*decl-specifier-seq*](dcl.spec.general#nt:decl-specifier-seq "9.2.1 General [dcl.spec.general]") of a prospective destructor declaration (if any)
|
||
shall befriend,inline,virtual, orconstexpr[.](#1.sentence-3)
|
||
|
||
[2](#2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/classes.tex#L2120)
|
||
|
||
If a class has no user-declared
|
||
prospective destructor,
|
||
a prospective destructor is implicitly
|
||
declared as defaulted ([[dcl.fct.def]](dcl.fct.def "9.6 Function definitions"))[.](#2.sentence-1)
|
||
|
||
An implicitly-declared prospective destructor is an
|
||
inline public member of its class[.](#2.sentence-2)
|
||
|
||
[3](#3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/classes.tex#L2130)
|
||
|
||
An implicitly-declared prospective destructor for a class X will have the form~X()
|
||
|
||
[4](#4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/classes.tex#L2136)
|
||
|
||
At the end of the definition of a class,
|
||
overload resolution is performed
|
||
among the prospective destructors declared in that class
|
||
with an empty argument list
|
||
to select the [*destructor*](#def:destructor "11.4.7 Destructors [class.dtor]") for the class,
|
||
also known as the [*selected destructor*](#def:destructor,selected "11.4.7 Destructors [class.dtor]")[.](#4.sentence-1)
|
||
|
||
The program is ill-formed if overload resolution fails[.](#4.sentence-2)
|
||
|
||
Destructor selection does not constitute
|
||
a reference to,
|
||
or odr-use ([[basic.def.odr]](basic.def.odr#term.odr.use "6.3 One-definition rule")) of,
|
||
the selected destructor,
|
||
and in particular,
|
||
the selected destructor may be deleted ([[dcl.fct.def.delete]](dcl.fct.def.delete "9.6.3 Deleted definitions"))[.](#4.sentence-3)
|
||
|
||
[5](#5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/classes.tex#L2151)
|
||
|
||
The address of a destructor shall not be taken[.](#5.sentence-1)
|
||
|
||
[*Note [1](#note-1)*:
|
||
|
||
A return statement in the body of a destructor
|
||
cannot specify a return value ([[stmt.return]](stmt.return "8.8.4 The return statement"))[.](#5.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
A destructor can be invoked for aconst,volatile orconstvolatile object[.](#5.sentence-3)
|
||
|
||
const andvolatile semantics ([[dcl.type.cv]](dcl.type.cv "9.2.9.2 The cv-qualifiers")) are not applied on an object under destruction[.](#5.sentence-4)
|
||
|
||
They stop being in effect when the destructor for the
|
||
most derived object ([[intro.object]](intro.object "6.8.2 Object model")) starts[.](#5.sentence-5)
|
||
|
||
[6](#6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/classes.tex#L2175)
|
||
|
||
[*Note [2](#note-2)*:
|
||
|
||
A declaration of a destructor that does not have a [*noexcept-specifier*](except.spec#nt:noexcept-specifier "14.5 Exception specifications [except.spec]") has the same exception specification as if it had been implicitly declared ([[except.spec]](except.spec "14.5 Exception specifications"))[.](#6.sentence-1)
|
||
|
||
â *end note*]
|
||
|
||
[7](#7)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/classes.tex#L2181)
|
||
|
||
A defaulted destructor for a class X is defined as deleted if
|
||
|
||
- [(7.1)](#7.1)
|
||
|
||
X is a non-union class and
|
||
any non-variant potentially constructed subobject has class type M (or possibly multidimensional array thereof) where M has a destructor that is deleted or
|
||
is inaccessible from the defaulted destructor,
|
||
|
||
- [(7.2)](#7.2)
|
||
|
||
X is a union and
|
||
* [(7.2.1)](#7.2.1)
|
||
|
||
overload resolution to select a constructor to
|
||
default-initialize an object of type X either fails or
|
||
selects a constructor that is either deleted or not trivial, or
|
||
|
||
* [(7.2.2)](#7.2.2)
|
||
|
||
X has a variant member V of
|
||
class type M (or possibly multi-dimensional array thereof)
|
||
where V has a default member initializer and M has a destructor that is non-trivial, or,
|
||
|
||
- [(7.3)](#7.3)
|
||
|
||
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[.](#7.sentence-1)
|
||
|
||
[8](#8)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/classes.tex#L2210)
|
||
|
||
A destructor for a class X is trivial if it is not user-provided and if
|
||
|
||
- [(8.1)](#8.1)
|
||
|
||
the destructor is not virtual,
|
||
|
||
- [(8.2)](#8.2)
|
||
|
||
all of the direct base classes of X have trivial destructors, and
|
||
|
||
- [(8.3)](#8.3)
|
||
|
||
either X is a union or
|
||
for all of the non-variant non-static data members of X that are of class
|
||
type (or array thereof), each such class has a trivial destructor[.](#8.sentence-1)
|
||
|
||
Otherwise, the destructor is[*non-trivial*](#def:destructor,non-trivial "11.4.7 Destructors [class.dtor]")[.](#8.sentence-2)
|
||
|
||
[9](#9)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/classes.tex#L2225)
|
||
|
||
A defaulted destructor is a constexpr destructor
|
||
if it is constexpr-suitable ([[dcl.constexpr]](dcl.constexpr "9.2.6 The constexpr and consteval specifiers"))[.](#9.sentence-1)
|
||
|
||
[10](#10)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/classes.tex#L2229)
|
||
|
||
Before a
|
||
defaulted destructor for a class is implicitly defined, all the non-user-provided
|
||
destructors for its base classes and its non-static data members are
|
||
implicitly defined[.](#10.sentence-1)
|
||
|
||
[11](#11)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/classes.tex#L2235)
|
||
|
||
A prospective destructor can be
|
||
declared virtual ([[class.virtual]](class.virtual "11.7.3 Virtual functions"))
|
||
and with a [*pure-specifier*](class.mem.general#nt:pure-specifier "11.4.1 General [class.mem.general]") ([[class.abstract]](class.abstract "11.7.4 Abstract classes"))[.](#11.sentence-1)
|
||
|
||
If the destructor of a class is virtual and
|
||
any objects of that class or any derived class are created in the program,
|
||
the destructor shall be defined[.](#11.sentence-2)
|
||
|
||
[12](#12)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/classes.tex#L2245)
|
||
|
||
[*Note [3](#note-3)*:
|
||
|
||
Some language constructs have special semantics when used during destruction;
|
||
see [[class.cdtor]](class.cdtor "11.9.5 Construction and destruction")[.](#12.sentence-1)
|
||
|
||
â *end note*]
|
||
|
||
[13](#13)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/classes.tex#L2252)
|
||
|
||
After executing the body of the destructor and destroying
|
||
any objects with automatic storage duration allocated within the body, a
|
||
destructor for classX calls the destructors forX's
|
||
direct non-variant non-static data members other than anonymous unions,
|
||
the destructors forX's
|
||
non-virtual direct base classes and, ifX is the most derived class ([[class.base.init]](class.base.init "11.9.3 Initializing bases and members")),
|
||
its destructor calls the destructors forX's
|
||
virtual base classes[.](#13.sentence-1)
|
||
|
||
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[.](#13.sentence-2)
|
||
|
||
Bases and members are destroyed in the reverse order of the completion of
|
||
their constructor (see [[class.base.init]](class.base.init "11.9.3 Initializing bases and members"))[.](#13.sentence-3)
|
||
|
||
[*Note [4](#note-4)*:
|
||
|
||
Areturn statement ([[stmt.return]](stmt.return "8.8.4 The 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[.](#13.sentence-4)
|
||
|
||
â *end note*]
|
||
|
||
Destructors for elements of an array are called in reverse order of their
|
||
construction (see [[class.init]](class.init "11.9 Initialization"))[.](#13.sentence-5)
|
||
|
||
[14](#14)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/classes.tex#L2287)
|
||
|
||
A destructor is invoked implicitly
|
||
|
||
- [(14.1)](#14.1)
|
||
|
||
for a constructed object with static storage duration ([[basic.stc.static]](basic.stc.static "6.8.6.2 Static storage duration")) at program termination ([[basic.start.term]](basic.start.term "6.10.3.4 Termination")),
|
||
|
||
- [(14.2)](#14.2)
|
||
|
||
for a constructed object with thread storage duration ([[basic.stc.thread]](basic.stc.thread "6.8.6.3 Thread storage duration")) at thread exit,
|
||
|
||
- [(14.3)](#14.3)
|
||
|
||
for a constructed object with automatic storage duration ([[basic.stc.auto]](basic.stc.auto "6.8.6.4 Automatic storage duration")) when the block in which an object is created exits ([[stmt.dcl]](stmt.dcl "8.10 Declaration statement")),
|
||
|
||
- [(14.4)](#14.4)
|
||
|
||
for a constructed temporary object when its lifetime ends ([[conv.rval]](conv.rval "7.3.5 Temporary materialization conversion"), [[class.temporary]](class.temporary "6.8.7 Temporary objects"))[.](#14.sentence-1)
|
||
|
||
In each case, the context of the invocation is the context of the construction of
|
||
the object[.](#14.sentence-2)
|
||
|
||
A destructor may also be invoked implicitly through use of a[*delete-expression*](expr.delete#nt:delete-expression "7.6.2.9 Delete [expr.delete]") ([[expr.delete]](expr.delete "7.6.2.9 Delete")) for a constructed object allocated
|
||
by a [*new-expression*](expr.new#nt:new-expression "7.6.2.8 New [expr.new]") ([[expr.new]](expr.new "7.6.2.8 New")); the context of the invocation is the[*delete-expression*](expr.delete#nt:delete-expression "7.6.2.9 Delete [expr.delete]")[.](#14.sentence-3)
|
||
|
||
[*Note [5](#note-5)*:
|
||
|
||
An array of class type contains several subobjects for each of which
|
||
the destructor is invoked[.](#14.sentence-4)
|
||
|
||
â *end note*]
|
||
|
||
A destructor can also be invoked explicitly[.](#14.sentence-5)
|
||
|
||
A destructor is [*potentially invoked*](#def:potentially_invoked "11.4.7 Destructors [class.dtor]") if it is invoked or as specified in [[expr.new]](expr.new "7.6.2.8 New"),[[stmt.return]](stmt.return "8.8.4 The return statement"), [[dcl.init.aggr]](dcl.init.aggr "9.5.2 Aggregates"),[[class.base.init]](class.base.init "11.9.3 Initializing bases and members"), and [[except.throw]](except.throw "14.2 Throwing an exception")[.](#14.sentence-6)
|
||
|
||
A program is ill-formed if a destructor that is potentially invoked is deleted
|
||
or not accessible from the context of the invocation[.](#14.sentence-7)
|
||
|
||
[15](#15)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/classes.tex#L2319)
|
||
|
||
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]](expr.delete "7.6.2.9 Delete"))[.](#15.sentence-1)
|
||
|
||
If the lookup fails or if the deallocation function has
|
||
a deleted definition ([[dcl.fct.def]](dcl.fct.def "9.6 Function definitions")), the program is ill-formed[.](#15.sentence-2)
|
||
|
||
[*Note [6](#note-6)*:
|
||
|
||
This assures that a deallocation function corresponding to the dynamic type of an
|
||
object is available for the[*delete-expression*](expr.delete#nt:delete-expression "7.6.2.9 Delete [expr.delete]") ([[class.free]](class.free "11.4.11 Allocation and deallocation functions"))[.](#15.sentence-3)
|
||
|
||
â *end note*]
|
||
|
||
[16](#16)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/classes.tex#L2332)
|
||
|
||
In an explicit destructor call, the destructor is specified by a~ followed by a[*type-name*](dcl.type.simple#nt:type-name "9.2.9.3 Simple type specifiers [dcl.type.simple]") or [*computed-type-specifier*](dcl.type.simple#nt:computed-type-specifier "9.2.9.3 Simple type specifiers [dcl.type.simple]") that denotes the destructor's class type[.](#16.sentence-1)
|
||
|
||
The invocation of a destructor is subject to the usual rules for member
|
||
functions ([[class.mfct]](class.mfct "11.4.2 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[.](#16.sentence-2)
|
||
|
||
[*Note [7](#note-7)*:
|
||
|
||
Invoking delete on a null pointer does not call the
|
||
destructor; see [[expr.delete]](expr.delete "7.6.2.9 Delete")[.](#16.sentence-3)
|
||
|
||
â *end note*]
|
||
|
||
[*Example [1](#example-1)*: 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 [8](#note-8)*:
|
||
|
||
An explicit destructor call must always be written using
|
||
a member access operator ([[expr.ref]](expr.ref "7.6.1.5 Class member access")) or a [*qualified-id*](expr.prim.id.qual#nt:qualified-id "7.5.5.3 Qualified names [expr.prim.id.qual]") ([[expr.prim.id.qual]](expr.prim.id.qual "7.5.5.3 Qualified names"));
|
||
in particular, the[*unary-expression*](expr.unary.general#nt:unary-expression "7.6.2.1 General [expr.unary.general]")~X() in a member function is not an explicit destructor call ([[expr.unary.op]](expr.unary.op "7.6.2.2 Unary operators"))[.](#16.sentence-4)
|
||
|
||
â *end note*]
|
||
|
||
[17](#17)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/classes.tex#L2380)
|
||
|
||
[*Note [9](#note-9)*:
|
||
|
||
Explicit calls of destructors are rarely needed[.](#17.sentence-1)
|
||
|
||
One use of such calls is for objects placed at specific
|
||
addresses using a placement[*new-expression*](expr.new#nt:new-expression "7.6.2.8 New [expr.new]")[.](#17.sentence-2)
|
||
|
||
Such use of explicit placement and destruction of objects can be necessary
|
||
to cope with dedicated hardware resources and for writing memory management
|
||
facilities[.](#17.sentence-3)
|
||
|
||
[*Example [2](#example-2)*: 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 example*]
|
||
|
||
â *end note*]
|
||
|
||
[18](#18)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/classes.tex#L2409)
|
||
|
||
Once a destructor is invoked for an object, the object's lifetime ends;
|
||
the behavior is undefined if the destructor is invoked
|
||
for an object whose lifetime has ended ([[basic.life]](basic.life "6.8.4 Lifetime"))[.](#18.sentence-1)
|
||
|
||
[*Example [3](#example-3)*:
|
||
|
||
If the destructor for an object with automatic storage duration 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[.](#18.sentence-2)
|
||
|
||
â *end example*]
|
||
|
||
[19](#19)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/classes.tex#L2419)
|
||
|
||
[*Note [10](#note-10)*:
|
||
|
||
The notation for explicit call of a destructor can be used for any scalar type
|
||
name ([[expr.prim.id.dtor]](expr.prim.id.dtor "7.5.5.5 Destruction"))[.](#19.sentence-1)
|
||
|
||
Allowing this makes it possible to write code without having to know if a
|
||
destructor exists for a given type[.](#19.sentence-2)
|
||
|
||
For example:typedef int I;
|
||
I* p;
|
||
p->I::~I();
|
||
|
||
â *end note*]
|
||
|
||
[20](#20)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/classes.tex#L2434)
|
||
|
||
A destructor shall not be a coroutine[.](#20.sentence-1)
|