Defining storage for static data members
Because static data has a single
piece of storage regardless of how many objects are created, that storage must
be defined in a single place. The compiler will not allocate storage for you.
The linker will report an error if a static data member is declared but
not defined.
The definition must occur outside the
class (no inlining is allowed), and only one definition is allowed. Thus, it is
common to put it in the implementation file for the class. The syntax sometimes
gives people trouble, but it is actually quite logical. For example, if you
create a static data member inside a class like this:
class A {
static int i;
public:
//...
};
Then you must define storage for that
static data member in the definition file like this:
int A::i = 1;
If you were to define an ordinary global
variable, you would say
int i = 1;
but here, the scope resolution operator
and the class name are used to specify A::i.
Some people have trouble with the idea
that A::i is private, and yet here’s something that seems to
be manipulating it right out in the open. Doesn’t this break the
protection mechanism? It’s a completely safe practice for two reasons.
First, the only place this initialization is legal is in the definition. Indeed,
if the static data were an object with a constructor, you would call the
constructor instead of using the = operator. Second, once the definition
has been made, the end-user cannot make a second definition – the linker
will report an error. And the class creator is forced to create the definition
or the code won’t link during testing. This ensures that the definition
happens only once and that it’s in the hands of the class
creator.
The entire initialization expression for
a static member is in the scope
of the class. For example,
//: C10:Statinit.cpp
// Scope of static initializer
#include <iostream>
using namespace std;
int x = 100;
class WithStatic {
static int x;
static int y;
public:
void print() const {
cout << "WithStatic::x = " << x << endl;
cout << "WithStatic::y = " << y << endl;
}
};
int WithStatic::x = 1;
int WithStatic::y = x + 1;
// WithStatic::x NOT ::x
int main() {
WithStatic ws;
ws.print();
} ///:~
Here, the qualification
WithStatic:: extends the scope of WithStatic to the entire
definition.