16

Yet another static question. I have read the following:

And I still fail to understand the following behavior: I have one h file:

// StaticTest.h
#include <stdio.h>

static int counter = 0;

struct A {
    A () {
        counter++;
        printf("In A's ctor(%d)\n", counter);
    }
    ~A () {
        counter--;
        printf("In A's dtor(%d)\n", counter);
    }
};

static A a;

And two cpp files:

// StaticTest1.cpp
#include "StaticTest.h"

int main () {
 return 0;
}

And:

// StaticTest2.cpp
#include "StaticTest.h"

The output of the program is:

In A's ctor(1)
In A's ctor(2)
In A's dtor(1)
In A's dtor(0)

Now, A's constructor is called twice, since the h file is included twice, and since A's instance named a is declared static, it has internal linkage and the compiler is happy. Since the counter is also declared static, it also has internal linkage, and I would expect that it's value will not be shared in the two cpp files --- but the program output implies the value is shared, since it counts up to 2.

any insights?

EDIT: Any answers regarding what is considered a "good programming habit" in the context of declaring static variables in h vs. cpp files is also welcomed.

Community
  • 1
  • 1
Itamar Katz
  • 9,544
  • 5
  • 42
  • 74
  • I *want* to say something about implementation details and about compile-time-initialized static values versus runtime-initialized static values, but I don't feel confident about posting it as an answer. Try leaving `counter` uninitialized and initializing it in `main()`. – Ignacio Vazquez-Abrams Nov 25 '10 at 12:08
  • @ Benoit Thiery: This IS the complete source code. – Itamar Katz Nov 25 '10 at 12:11
  • @ Ignacio Vazquez-Abrams: from MSDN's page: "When you declare a variable, the variable has static duration and the compiler initializes it to 0 unless you specify another value" – Itamar Katz Nov 25 '10 at 12:14
  • 1
    "Any answers regarding what is considered a "good programming habit"" - (1) don't use mutable globals. (2) if you do, refer to them only from one TU and define them in that .cpp file. (3) if you really want a separate mutable global for each TU then don't violate the One Definition Rule by referring to them from a definition that's shared between TUs. (4) one mutable global is hard enough to manage, why on earth would you let clients of your library create as many of them as they like, willy-nilly, all with the same name? ;-) – Steve Jessop Mar 07 '11 at 12:08

1 Answers1

15

If StaticTest.h is shared between difference source files then you will get undefined behaviour.

If you define a class or inline functions in different translation units then their definitions must be the same (same sequence of tokens) and, crucially, any identifiers must refer to the same entity (unless a const object with internal linkage) as in the definition in another translation unit.

You violate this because counter has internal linkage so in different translation units the identifier in the function definitions refers to a different object.

Reference: C++03 3.2 [basic.def.odr] / 5.

CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • 1
    I am not sure I completely understand your answer - `counter` is not a class definition, it is a variable declaration, and anyway, isn't internal linkage means I can have two entities, each one in it's own translation unit? – Itamar Katz Nov 25 '10 at 12:20
  • 3
    @Itamar Katz: Yes, you have more than one entity called `counter` (each with internal linkage), but you are violating the ODR on the definition of `A`, `A::A` and `A::~A`. Although your definitions consist of the same token sequences (good), because `counter` has internal linkage, when you use that identifier in the definition of `A` it refers to a different object in each separate translation unit. This means that the definitions of `A` are _not_ the same across translation units which violates the ODR. – CB Bailey Nov 25 '10 at 12:23
  • And another comment/question - Does it follow from your answer that using the `static` keyword in an `h` file which is included by more than one `cpp`, will always produce an undefined behaviour? – Itamar Katz Nov 25 '10 at 12:26
  • @Itamar Katz: No, there's nothing illegal about declaring something `static` in a header file, it's just the use of an entity with internal linkage in a class or inline function definition that automatically leads to undefined behaviour when included in a second translation unit because such an identifier will necessarily refer to a different entity in each TU. – CB Bailey Nov 25 '10 at 12:29
  • 3
    No, the problem comes because name of a class and it's members have external linkage. So there should be one and only definition of the member functions of the class. That being the case, which counter should it update? Therefore UB if ODR is violated. However, using a static namespace scope variable inside of a function with internal linkage should be fine and no UB. – Chubsdad Nov 25 '10 at 12:30
  • @Charles Bailey: That's a brilliant answer +1 – Chubsdad Nov 25 '10 at 12:32