views:

3571

answers:

3

If a variable is declared as static in a function's scope it is only initialized once and retains its value between function calls, we all know that but what exactly is its lifetime? When do its constructor and destructor get called?

void foo() 
{ 
    static string plonk = "When will I die?";
}


P.S. For those who want to know why I asked the question if I already knew the answer?

+10  A: 

The lifetime of function static variables begins the first time[0] the program flow encounters the declaration and it ends at program termination. This means that the run-time must perform some book keeping in order to destruct it only if it was actually constructed.

Additionally since the standard says that the destructors' of global objects must run in the reverse order of the completion of their construction[1] and the order of construction may depend on the specific program run the order of construction must be taken into account.

Example

struct emitter {
    string str;
    emitter(const string& s) : str(s) { cout << "Created " << str; << endl; }
    ~emitter() { cout << "Destroyed " << str << endl; }
};

void foo(bool skip_first) 
{
    if (!skip_first)
        static emitter a("in if");
    static emitter b("in foo");
}

int main(int argc, char*[])
{
    foo(argc != 2);
    if (argc == 3)
        foo(false);
}

Output:

C:>sample.exe
Created in foo
Destroyed in foo

C:>sample.exe 1
Created in if
Created in foo
Destroyed in foo
Destroyed in if

C:>sample.exe 1 2
Created in foo
Created in if
Destroyed in if
Destroyed in foo

[0] Since C++98 has no reference to multiple threads how this will be behave in a multi-threaded environment is unspecified, and can be problematic as Roddy mentions.

[1] C++98 section 3.6.3.1 [basic.start.term]

Motti
+14  A: 

Motti is right about the order, but there are some other things to consider:

Compilers typically use a hidden flag variable to indicate if the local statics have already been initialized, and this flag is checked on every entry to the function. Obviously this is a small performance hit, but what's more of a concern is that this flag is not guaranteed to be thread-safe.

If you have a local static as above, and 'foo' is called from multiple threads, you may have race conditions causing 'plonk' to be initialized incorrectly or even multiple times. Also, in this case 'plonk' may get destructed by a different thread than the one which constructed it.

Despite what the standard says, I'd be very wary of the actual order of local static destruction, because it's possible that you may unwittingly rely on a static being still valid after it's been destructed, and this is really difficult to track down.

Roddy
C++0x requires that static initialization be thread safe. So be wary but things will only get better.
caspin
Destruction order issues can be avoided with a little policy. static/global objects (singletons, etc) shall not access other static objects in their method bodies. They shall only accessed in constructors where a reference/pointer can be stored for later access in methods. This is not perfect but should fix 99 of the cases and cases it doesn't catch are obviously fishy and should be caught in a code review. This is still not a perfect fix as the policy cannot be enforced in the language
caspin
+2  A: 

FWIW, Codegear C++Builder doesn't destruct in the expected order according to the standard.

C:\> sample.exe 1 2
Created in foo
Created in if
Destroyed in foo
Destroyed in if

... which is another reason not to rely on the destruction order!

Roddy
Not a good argument. I would say this is more of an argument not to use this compiler.
Martin York
Hmm. If you're interested in producing real-world portable code, rather than just theoretically portable code, I think it's useful to know what areas of the language can cause problems. I'd be surprised if C++Builder was unique in not handling this.
Roddy
I'd agree, except that I'd phrase it as "what compilers cause problems, and what areas of the language they do it in" ;-P
Steve Jessop