Compilers can produce different sorts of code.
Static initialized data segment
The compiler emits into a data section a name and its initial value.
.data
dw myData 6
This is initialized at compile time, and is safely defined throughout the life of the program
constructed data
Another alternative is for the compiler to reserve some space for the variable, and create an initializer/constructor for the data, and then call the constructor just before main
. With the destructor (if required) being performed atexit
.
class CriticalSection {
CRITICAL_SECTION m_myCS;
public:
CriticalSection() {
InitializeCriticalSection( &m_myCS );
}
~CriticalSection() {
DeleteCriticalSection( & m_myCS );
}
} cs;
combined
Some data may be performed in both stages.
struct Data {
bool initialized;
void *(*pMalloc)( size_t size );
} FixMalloc = { true, MyMalloc };
I have seen compilers (VS2013) produce code which initializes initialized
to true in the static data, but creates a function to assign pMalloc
to MyMalloc
at runtime. (This was because there was not a known constant for MyMalloc.)
singleton method
SomeClass * GetSomeClass()
{
static SomeClass cls;
return &cls;
}
This is order defined - when it gets called, but requires a fully C++11
compiler to be thread safe.
Summary
The guarantees are :-
- statics in the same compilation unit are initialized top-to-bottom.
- statics are initialized single threaded.
- singletons have defined order of construction. But not necessarily thread safe.
The guarantees are not :-
- All of an object is initialized at the same time.
- Static initialization has a working runtime.
Before main is called, both your statics and the C/C++ runtime are bootstrapping. The order of construction issues also occur between your code and the runtime, and so you may construct some items with complex constructors which rely on services which can't be available.