Static initialization dependency
Within a specific translation unit, the
order of initialization of static objects is guaranteed to be the order in which
the object definitions appear in that translation unit.
The order of destruction is guaranteed to be the reverse of the order of
initialization.
However, there is no guarantee concerning
the order of initialization of static objects across translation units,
and the language provides no way to specify this order. This can cause
significant problems. As an example of an instant disaster (which will halt
primitive operating systems and kill the process on sophisticated ones), if one
file contains
//: C10:Out.cpp {O}
// First file
#include <fstream>
std::ofstream out("out.txt"); ///:~
and another file uses the out
object in one of its initializers
//: C10:Oof.cpp
// Second file
//{L} Out
#include <fstream>
extern std::ofstream out;
class Oof {
public:
Oof() { std::out << "ouch"; }
} oof;
int main() {} ///:~
the program may work, and it may not. If
the programming environment builds the program so that the first file is
initialized before the second file, then there will be no problem. However, if
the second file is initialized before the first, the constructor for Oof
relies upon the existence of out, which hasn’t been constructed yet
and this causes chaos.
This problem only occurs with static
object initializers that depend on each other. The statics in a
translation unit are initialized before the first invocation of a function in
that unit – but it could be after main( ). You can’t be
sure about the order of initialization of static objects if they’re in
different files.
A subtler example can be found in the
ARM.[47]
In one file you have at the global scope:
extern int y;
int x = y + 1;
and in a second file you have at the
global scope:
extern int x;
int y = x + 1;
For all static objects, the
linking-loading mechanism guarantees a static initialization to
zero before the dynamic
initialization specified by the programmer takes place. In the previous example,
zeroing of the storage occupied by the fstream out object has no special
meaning, so it is truly undefined until the constructor is called. However, with
built-in types, initialization to zero does have meaning, and if the
files are initialized in the order they are shown above, y begins as
statically initialized to zero, so x becomes one, and y is
dynamically initialized to two. However, if the files are initialized in the
opposite order, x is statically initialized to zero, y is
dynamically initialized to one, and x then becomes two.
Programmers must be aware of this because
they can create a program with static initialization dependencies and get it
working on one platform, but move it to another compiling environment where it
suddenly, mysteriously, doesn’t
work.