Initialization of Static
Nonlocal Objects

Tutorials

 

Overview      

 The Problem     

The Solution     

Tutorial Index 

 

Overview

The section 3.6.2: Initialization of non-local objects of the ISO standard is the ultimate reference.

A Nonlocal static object is either a global static object or a file-scope static object.

The default initialization of all static objects to zero is done before any run time initialization is done.

Static objects of POD (Plain Old Data) types initialized with constant expressions shall be initialized before any run time initialization is done.

Static objects of namespace scope (objects declared inside of namespaces and what C calls file-scope objects) defined in the same translation unit and initialized at run time shall be initialized in the order that their definitions appear in the translation unit.

No further order order is imposed by the standard on the initialization of objects from different translation units. The reason that no further order is defined is because the standard allows circular references. Consider these two files:

//file_1.c
    extern int f2;
    int f1 = f2 + 1;

//file_2.c
    extern int f1;
    int f2 = f1 + 1;

A circular reference exists between the two files. Static initialization of both f1 and f2 to zero would occur first. Then, in an implementation dependent fashion, the constructor for f1 or f2 would be called. Finally, the remaining constructor would be called. So the result is either

f1==1 && f2==2 or f1==2 && f1==1

Either result is possible. Both results are standard conforming.


The Problem

Its a problem if you have a static object in one file that has a run time initializer, and a static object in another file with a run time initializer, and one of the objects must be initialized before the other.

Say you are writing a class that needs to have some run time initialization done before it can execute correctly. Further assume that you are following "good programming practice" by putting the declarations and definitions for this class in its own files. You code a constructor for another class, and casually write an invocation of a member function of the first class. You may have just created a Nonlocal Static Object Initialization problem.


The Solution - Nifty Counter

This solution is quoted from "The Annotated C++ Reference Manual", by Margaret A. Ellis and Bjarne Stroustrup, Addison-Wesley Publishing Company, ISBN 0-201-51259-1, Section 3.4, page 20-21.

"A useful technique for ensuring that a global object is initialized only once and before its first use is to maintain a count of the number of translation units using it. For example, consider writing a library that provides some statically allocated objects that must be initialized before the first use of the facilities of the libraries.

// file nifty_library.h:

// ...
extern X1 obj1;
//...
extern Xn objn;

"If these objects require dynamic initialization, we have a problem: the dynamic initialization of the translation unit or translations units holding the definitions cannot (except by special system-dependent and extra-linguistic magic) be assumed to be performed before the dynamic initialization of all users. To solve this problem we add to nifty_library.h

class nifty_counter {
    static count;
public:
    nifty_counter()
    {
        if (count++ == 0) {
            // initialize obj1 ... objn
        }
    }
    ~nifty_counter()
    {
        if (--count == 0) {
            // clean up obj1 ... objn
        }
    }
};
static nifty_counter nifty;

"Now every file that includes the library header also creates a nifty_counter object and initializes it with the effect of increasing nifty_counter::count. The first time this happens the library objects will be initialized. Since the library header appears before any use of the library facilities this ensures proper initialization. Since destruction is done in reverse order of construction, this technique also ensures that cleanup is done after the last use of the library.

"We believe this technique has been independently discovered several times. Jerry Schwarz pioneered its use in C++ by using it for construction and destruction of cout, cin, and others in the iostream library. These objects are constructed before the first I/O operation, and destroyed (with the effect of flushing their buffers) after the last."


Copyright © 1996-1999 by Kuck & Associates, Inc. All rights reserved.

E-Mail KAI Technical Support   E-Mail KAI   Contact KAI   

This file last updated on 4 June 1999.