to solve the problem.
Technique one
This technique was pioneered by Jerry
Schwarz while creating the iostream library (because the
definitions for cin, cout, and cerr are static and
live in a separate file). It’s actually inferior to the second technique
but it’s been around a long time and so you may come across code that uses
it; thus it’s important that you understand how it works.
This technique requires an additional
class in your library header file. This class is responsible for the dynamic
initialization of your library’s static objects. Here is a simple
example:
//: C10:Initializer.h
// Static initialization technique
#ifndef INITIALIZER_H
#define INITIALIZER_H
#include <iostream>
extern int x; // Declarations, not definitions
extern int y;
class Initializer {
static int initCount;
public:
Initializer() {
std::cout << "Initializer()" << std::endl;
// Initialize first time only
if(initCount++ == 0) {
std::cout << "performing initialization"
<< std::endl;
x = 100;
y = 200;
}
}
~Initializer() {
std::cout << "~Initializer()" << std::endl;
// Clean up last time only
if(--initCount == 0) {
std::cout << "performing cleanup"
<< std::endl;
// Any necessary cleanup here
}
}
};
// The following creates one object in each
// file where Initializer.h is included, but that
// object is only visible within that file:
static Initializer init;
#endif // INITIALIZER_H ///:~
The declarations for x and
y announce only that these objects exist, but they don’t allocate
storage for the objects. However, the definition for the Initializer init
allocates storage for that object in every file where the header is included.
But because the name is static (controlling visibility this time, not the
way storage is allocated; storage is at file scope by default), it is visible
only within that translation unit, so the linker will not complain about
multiple definition errors.
Here is the file containing the
definitions for x, y, and initCount:
//: C10:InitializerDefs.cpp {O}
// Definitions for Initializer.h
#include "Initializer.h"
// Static initialization will force
// all these values to zero:
int x;
int y;
int Initializer::initCount;
///:~
(Of course, a file static instance of
init is also placed in this file when the header is included.) Suppose
that two other files are created by the library user:
//: C10:Initializer.cpp {O}
// Static initialization
#include "Initializer.h"
///:~
//: C10:Initializer2.cpp
//{L} InitializerDefs Initializer
// Static initialization
#include "Initializer.h"
using namespace std;
int main() {
cout << "inside main()" << endl;
cout << "leaving main()" << endl;
} ///:~
Now it doesn’t matter which
translation unit is initialized first. The first time a translation unit
containing Initializer.h is initialized, initCount will be zero so
the initialization will be performed. (This depends heavily on the fact that the
static storage area is set to zero before any dynamic initialization takes
place.) For all the rest of the translation units, initCount will be
nonzero and the initialization will be skipped. Cleanup happens in the reverse
order, and ~Initializer( ) ensures that it will happen only
once.
This example used built-in types as the
global static objects. The technique also works with classes, but those objects
must then be dynamically initialized by the Initializer class. One way to
do this is to create the classes without constructors and destructors, but
instead with initialization and cleanup member functions using different names.
A more common approach, however, is to have pointers to objects and to create
them using new inside Initializer( ).