FAQs in section [6]:
[6.1] What's the deal with destructors?
A destructor gives an object its last rites.
Destructors are used to release any resources allocated by the
object. E.g., class Lock might lock a semaphore,
and the destructor will release that semaphore. The most common
example is when the constructor uses new, and the
destructor uses delete.
Destructors are a "prepare to die" member function.
They are often abbreviated "dtor".
[6.2] What's the order that local objects are destructed?
In reverse order of construction: First constructed, last
destructed.
In the following example, b's destructor will be
executed first, then a's destructor:
void userCode()
{
Fred a;
Fred b;
// ...
}
[6.3] What's the order that objects in an array are
destructed?
In reverse order of construction: First constructed, last
destructed.
In the following example, the order for destructors will be a[9],
a[8], ..., a[1], a[0]:
void userCode()
{
Fred a[10];
// ...
}
[6.4] Can I overload the destructor for my class?
No.
You can have only one destructor for a class Fred. It's
always called Fred::~Fred(). It never takes any
parameters, and it never returns anything.
You can't pass parameters to the destructor anyway, since you never explicitly call a destructor (well,
almost never).
[ Top | Bottom | Previous section | Next section ]
[6.5] Should I explicitly call a destructor on a local
variable?
No!
The destructor will get called again at the close }
of the block in which the local was created. This is a guarantee
of the language; it happens automagically; there's no way to stop
it from happening. But you can get really bad results
from calling a destructor on the same object a second time! Bang!
You're dead!
[6.6] What if I want a local to "die" before the
close } of the scope in which it was created? Can I call
a destructor on a local if I really want to?
No! [For context, please read the
previous FAQ].
Suppose the (desirable) side effect of destructing a local File
object is to close the File. Now suppose you have an
object f of a class File and you want File
f to be closed before the end of the scope (i.e., the })
of the scope of object f:
void someCode()
{
File f;
// ... [This code that should execute when f is still open] ...
// < We want the side-effect of f's destructor here!
// ... [This code that should execute after f is closed] ...
}
There is a simple solution to this
problem. But in the mean time, remember: Do not explicitly call the
destructor!
[6.7] OK, OK already; I won't explicitly call the destructor
of a local; but how do I handle the above situation?
[For context, please read the
previous FAQ].
Simply wrap the extent of the lifetime of the local in an
artificial block { ... }:
void someCode()
{
{
File f;
// ... [This code will execute when f is still open] ...
}
// ^ f's destructor will automagically be called here!
// ... [This code will execute after f is closed] ...
}
[6.8] What if I can't wrap the local in an artificial block?
Most of the time, you can limit the lifetime of a local by wrapping the local in an artificial
block ({ ... }). But if for some reason you
can't do that, add a member function that has a similar effect as
the destructor. But do not call the destructor itself!
For example, in the case of class File, you
might add a close() method. Typically the destructor
will simply call this close() method. Note that the close()
method will need to mark the File object so a subsequent
call won't re-close an already-closed File. E.g., it
might set the fileHandle_ data member to some
nonsensical value such as -1, and it might check at the beginning
to see if the fileHandle_ is already equal to -1:
class File {
public:
void close();
~File();
// ...
private:
int fileHandle_; // fileHandle_ >= 0 if/only-if it's open
};
File::~File()
{
close();
}
void File::close()
{
if (fileHandle_ >= 0) {
// ... [Perform some operating-system call to close the file] ...
fileHandle_ = -1;
}
}
Note that the other File methods may also need to
check if the fileHandle_ is -1 (i.e., check if the File
is closed).
[6.9] But can I explicitly call a destructor if I've
allocated my object with new?
Probably not.
Unless you used placement new,
you should simply delete the object rather than
explicitly calling the destructor. For example, suppose you
allocated the object via a typical new expression:
Fred* p = new Fred();
Then the destructor Fred::~Fred() will automagically
get called when you delete it via:
delete p; // Automagically calls p->~Fred()
You should not explicitly call the destructor, since
doing so won't release the memory that was allocated for the Fred
object itself. Remember: delete p
does two things: it calls the destructor and it deallocates
the memory.
[6.10] What is "placement new" and why
would I use it?
There are many uses of placement new. The simplest
use is to place an object at a particular location in memory.
This is done by supplying the place as a pointer parameter to the
new part of a new expression:
#include <new.h> // Must #include this to use "placement new"
#include "Fred.h" // Declaration of class Fred
void someCode()
{
char memory[sizeof(Fred)]; // Line #1
void* place = memory; // Line #2
Fred* f = new(place) Fred(); // Line #3 (see "DANGER" below)
// The pointers f and place will be equal
// ...
}
Line #1 creates an array of sizeof(Fred) bytes of
memory, which is big enough to hold a Fred object. Line
#2 creates a pointer place that points to the first byte
of this memory (experienced C programmers will note that this
step was unnecessary; it's there only to make the code more
obvious). Line #3 essentially just calls the constructor Fred::Fred().
The this pointer in the Fred constructor will
be equal to place. The returned pointer f will
therefore be equal to place.
ADVICE: Don't use this "placement new"
syntax unless you have to. Use it only when you really care that
an object is placed at a particular location in memory. For
example, when your hardware has a memory-mapped I/O timer device,
and you want to place a Clock object at that memory
location.
DANGER: You are taking sole responsibility
that the pointer you pass to the "placement new"
operator points to a region of memory that is big enough
and is properly aligned for the object type that you're creating.
Neither the compiler nor the run-time system make any attempt to
check whether you did this right. If your Fred class
needs to be aligned on a 4 byte boundary but you supplied a
location that isn't properly aligned, you can have a serious
disaster on your hands (if you don't know what "alignment"
means, please don't use the placement new
syntax). You have been warned.
You are also solely responsible for destructing the placed
object. This is done by explicitly calling the destructor:
void someCode()
{
char memory[sizeof(Fred)];
void* p = memory;
Fred* f = new(p) Fred();
// ...
f->~Fred(); // Explicitly call the destructor for the placed object
}
This is about the only time you ever explicitly call a
destructor.
[6.11] When I write a destructor, do I need to explicitly
call the destructors for my member objects?
No. You never need to explicitly call a destructor (except
with placement new).
A class's destructor (whether or not you explicitly define one)
automagically invokes the destructors for member objects.
They are destroyed in the reverse order they appear within the
declaration for the class.
class Member {
public:
~Member();
// ...
};
class Fred {
public:
~Fred();
// ...
private:
Member x_;
Member y_;
Member z_;
};
Fred::~Fred()
{
// Compiler automagically calls z_.~Member()
// Compiler automagically calls y_.~Member()
// Compiler automagically calls x_.~Member()
}
[6.12] When I write a derived class's destructor, do I need
to explicitly call the destructor for my base class?
No. You never need to explicitly call a destructor (except
with placement new).
A derived class's destructor (whether or not you explicitly
define one) automagically invokes the destructors for
base class subobjects. Base classes are destructed after member
objects. In the event of multiple inheritance, direct base
classes are destructed in the reverse order of their appearance
in the inheritance list.
class Member {
public:
~Member();
// ...
};
class Base {
public:
virtual ~Base(); // A virtual destructor
// ...
};
class Derived : public Base {
public:
~Derived();
// ...
private:
Member x_;
};
Derived::~Derived()
{
// Compiler automagically calls x_.~Member()
// Compiler automagically calls Base::~Base()
}
Note: Order dependencies with virtual inheritance are
trickier. If you are relying on order dependencies in a virtual
inheritance hierarchy, you'll need a lot more information than is
in this FAQ.
|