A destructor is a special member
function that is called when the lifetime of an object ends.
The purpose of the destructor is to free the resources that the object may have
acquired during its lifetime.
Syntax
~ class_name ();
|
(1)
|
||||||||
virtual ~ class_name ();
|
(2)
|
||||||||
decl-specifier-seq(optional) ~ class_name () = default;
|
(3)
|
(since C++11)
|
|||||||
decl-specifier-seq(optional) ~ class_name () = delete;
|
(4)
|
(since C++11)
|
|||||||
attr(optional) decl-specifier-seq(optional) id-expression ( void(optional) ) except(optional)attr(optional) ;
|
(5)
|
||||||||
1) Typical declaration
of a destructor
2) Virtual destructor
is usually required in a base class
3) Forcing a
destructor to be generated by the compiler
4) Disabling the
implicit destructor
5) Formal syntax of a
destructor declaration
decl-specifier-seq
|
-
|
friend, inline, virtual, or
nothing (no return type)
|
||
id-expression
|
-
|
within a class definition, the
symbol ~ followed by the class name. Within a class template,
the symbol ~ followed by the name of the current instantiation of
the template. At namespace scope or in a friend declaration
within a different class, nested-name-specifier followed by
the symbol ~ followed by the class name which is the same class
as the one named by the nested-name-specifier. In any case, the name must be
the actual name of the class or template, and not a typedef. The entire
id-expression may be surrounded by parentheses which do not change its
meaning.
|
||
attr(C++11)
|
-
|
optional sequence of any number of attributes
|
||
except
|
-
|
exception specification as in any
function declaration (either dynamic
exception specification(deprecated) or noexcept
specification(C++11))
|
Explanation
The destructor is called
whenever an object's lifetime ends,
which includes
§
program
termination, for objects with static storage
duration
§ thread exit, for objects with thread-local 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 may also be
called directly, e.g. to destroy an object that was constructed using placement-new or through an allocator
member function such as std::allocator::destroy(),
to destroy an object that was constructed through the allocator. Note that
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.
Implicitly-declared
destructor
If
no user-declared destructor is provided for a class type (struct, class, or union), the compiler will always declare a
destructor as an inline public member
of its class.
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).
Deleted
implicitly-declared destructor
The
implicitly-declared or defaulted destructor for class T is undefined (until C++11)defined as deleted (since C++11) if any of the
following is true:
§
T has a non-static data member that cannot be
destructed (has deleted or inaccessible destructor)
§
T has direct or virtual base class that cannot be
destructed (has deleted or inaccessible destructors)
§ T is a union and has a variant
member with non-trivial destructor.
|
(since C++11)
|
§
The implicitly-declared destructor is virtual (because
the base class has a virtual destructor) and the lookup for the deallocation
function (operator delete() results in a call to ambiguous,
deleted, or inaccessible function.
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.
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.
This implicitly-defined destructor has an empty body.
Destruction
sequence
For both user-defined or
implicitly-defined destructors, after the body of the destructor is executed,
the compiler calls the destructors for all non-static non-variant 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 (which in turn call the destructors of their
members and their base classes, etc), and then, if this object is of
most-derived class, it calls the destructors of all virtual bases.
Even
when the destructor is called directly (e.g. obj.~Foo();), the return statement in ~Foo() does not return control to the caller
immediately: it calls all those member and base destructors first.
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 destructor may be
declared pure virtual,
for example in a base class which needs to be made abstract, but has no other
suitable functions that could be declared pure virtual. Such destructor must
have a definition, since all base class destructors are always called when the
derived class is destroyed:
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 is
called instead.
Although std::uncaught_exception 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,
which rely on the ability of the destructors of nameless temporaries to throw
exceptions at the end of the full expression that constructs the temporary.
Example
#include <iostream>
#include<conio.h>
#include<string.h>
struct Student
{
int i;
Student ( int i ) : i ( i ) {}
~ Student ()
{
}
};
int main()
{
Student a1(1);
Student * p;
{ // nested scope
Student Student
2(2);
p = new Student (3);
} // a2 out of scope
delete p; // calls the destructor of a3
}
Output:
~ Student 2
~ Student 3
~ Student 1