Static object destructors
Destructors for static objects (that is,
all objects with static storage, not just local static objects as in the example
above) are called when main( ) exits or when the Standard C library
function
exit( )
is explicitly called. In most implementations, main( ) just
calls exit( ) when it terminates. This means that it can be
dangerous to call exit( ) inside a destructor because you can end up
with infinite recursion. Static object destructors are not called if you
exit the program using the Standard C library function
abort( ).
You can specify actions to take place
when leaving main( ) (or calling exit( )) by using the
Standard C library function
atexit( ).
In this case, the functions registered by atexit( ) may be called
before the destructors for any objects constructed before leaving
main( ) (or calling exit( )).
Like ordinary destruction, destruction of
static objects
occurs
in the reverse order of initialization. However, only objects that have been
constructed are destroyed. Fortunately, the C++ development tools keep track of
initialization order and the objects that have been constructed. Global objects
are always
constructed
before main( ) is entered and destroyed as main( )
exits, but if a function containing a local static object
is never
called, the constructor for that object is never executed, so the destructor is
also not executed. For example,
//: C10:StaticDestructors.cpp
// Static object destructors
#include <fstream>
using namespace std;
ofstream out("statdest.out"); // Trace file
class Obj {
char c; // Identifier
public:
Obj(char cc) : c(cc) {
out << "Obj::Obj() for " << c << endl;
}
~Obj() {
out << "Obj::~Obj() for " << c << endl;
}
};
Obj a('a'); // Global (static storage)
// Constructor & destructor always called
void f() {
static Obj b('b');
}
void g() {
static Obj c('c');
}
int main() {
out << "inside main()" << endl;
f(); // Calls static constructor for b
// g() not called
out << "leaving main()" << endl;
} ///:~
In Obj, the char c acts as
an identifier so the constructor and destructor can print out information about
the object they’re working on. The Obj a is a global object, so the
constructor is always called for it before main( ) is entered, but
the constructors for the static Obj b inside f( ) and the
static Obj c inside g( ) are called only if those functions
are called.
To demonstrate which constructors and
destructors are called, only f( ) is called. The output of the
program is
Obj::Obj() for a
inside main()
Obj::Obj() for b
leaving main()
Obj::~Obj() for b
Obj::~Obj() for a
The constructor for a is called
before main( ) is entered, and the constructor for b is
called only because f( ) is called. When main( ) exits,
the destructors for the objects that have been constructed are called in reverse
order of their construction. This means that if g( ) is
called, the order in which the destructors for b and c are called
depends on whether f( ) or g( ) is called
first.
Notice that the trace file
ofstream object out is also a static object – since it is
defined outside of all functions, it lives in the static storage
area. It is important that its definition (as opposed to
an extern declaration) appear at the beginning of the file, before there
is any possible use of out. Otherwise, you’ll be using an object
before it is properly initialized.
In C++, the constructor for a global
static object is called before main( ) is entered, so you now have a
simple and portable way to execute code before entering
main( ) and to
execute code with the destructor after exiting
main( ).
In C, this was always a trial that required you to root around in the compiler
vendor’s assembly-language startup
code.