C++ Programming
The static keyword can be used in four different ways:
- to create permanent storage for local variables in a function.
- to specify internal linkage.
- to declare member functions that act like non-member functions.
- to create a single copy of a data member.
The static
keyword can also be used on functions, inside functions, on classes, on classes members (data and functions), in structs, unions (but not in a union's member) we will cover each use separately.
Permanent storage
editUsing the static modifier makes a variable have static lifetime and on global variables makes them require internal linkage (variables will not be accessible from code of the same project that resides in other files).
- static lifetime
- Means that a static variable will need to be initialized in the file scope and at run time, will exist and maintain changes across until the program's process is closed, the particular order of destruction of static variables is undefined.
static
variables instances share the same memory location. This means that they keep their value between function calls. For example, in the following code, a static variable inside a function is used to keep track of how many times that function has been called:
void foo() {
static int counter = 0;
cout << "foo has been called " << ++counter << " times\n";
}
int main() {
for( int i = 0; i < 10; ++i ) foo();
}
- Internal linkage
When used on a free function, a global variable, or a global constant, it specifies internal linkage (as opposed to extern
, which specifies external linkage). Internal linkage limits access to the data or function to the current file.
Examples of use outside of any function or class:
static int apples = 15;
- defines a "static global" variable named apples, with initial value 15, only visible from this translation unit.
static int bananas;
- defines a "static global" variable named bananas, with initial value 0, only visible from this translation unit.
int g_fruit;
- defines a global variable named g_fruit, with initial value 0, visible from every translation unit. Such variables are often frowned on as poor style.
static const int muffins_per_pan=12;
- defines is a variable named muffins_per_pan, visible only in this translation unit. The static keyword is redundant here.
const int hours_per_day=24;
- defines a variable named hours_per_day, only visible in this translation unit. (This acts the same as ).
static const int hours_per_day=24;
static void f();
- declares that there is a function f taking no arguments and with no return value defined in this translation unit. Such a forward declaration is often used when defining mutually recursive functions.
static void f(){;}
- defines the function f() declared above. This function can only be called from other functions and members in this translation unit; it is invisible to other translation units.
static member function
editMember functions or variables declared static are shared between all instances of an object type. Meaning that only one copy of the member function or variable does exists for any object type.
- member functions callable without an object
When used in a class function member, the function does not take an instantiation as an implicit this
parameter, instead behaving like a free function. This means that static class functions can be called without creating instances of the class:
class Foo {
public:
Foo() {
++numFoos;
cout << "We have now created " << numFoos << " instances of the Foo class\n";
}
static int getNumFoos() {
return numFoos;
}
private:
static int numFoos;
};
int Foo::numFoos = 0; // allocate memory for numFoos, and initialize it
int main() {
Foo f1;
Foo f2;
Foo f3;
cout << "So far, we've made " << Foo::getNumFoos() << " instances of the Foo class\n";
}
Named constructors
editNamed constructors are a good example of using static member functions. Named constructors is the name given to functions used to create an object of a class without (directly) using its constructors. This might be used for the following:
- To circumvent the restriction that constructors can be overloaded only if their signatures differ.
- Making the class non-inheritable by making the constructors private.
- Preventing stack allocation by making constructors private
Declare a static member function that uses a private constructor to create the object and return it. (It could also return a pointer or a reference but this complication seems useless, and turns this into the factory pattern rather than a conventional named constructor.)
Here's an example for a class that stores a temperature that can be specified in any of the different temperature scales.
class Temperature
{
public:
static Temperature Fahrenheit (double f);
static Temperature Celsius (double c);
static Temperature Kelvin (double k);
private:
Temperature (double temp);
double _temp;
};
Temperature::Temperature (double temp):_temp (temp) {}
Temperature Temperature::Fahrenheit (double f)
{
return Temperature ((f + 459.67) / 1.8);
}
Temperature Temperature::Celsius (double c)
{
return Temperature (c + 273.15);
}
Temperature Temperature::Kelvin (double k)
{
return Temperature (k);
}
static data member
editThe use of the static
specifier in a data member, will cause that member to be shared by all instances of the owner class and derived classes. To use static data members you must declare the data member as static and initialize it outside of the class declaration, at file scope.
When used in a class data member, all instantiations of that class share one copy of the variable.
class Foo {
public:
Foo() {
++iNumFoos;
cout << "We have now created " << iNumFoos << " instances of the Foo class\n";
}
private:
static int iNumFoos;
};
int Foo::iNumFoos = 0; // allocate memory for numFoos, and initialize it
int main() {
Foo f1;
Foo f2;
Foo f3;
}
In the example above, the static class variable numFoos is shared between all three instances of the Foo class (f1, f2 and f3) and keeps a count of the number of times that the Foo class has been instantiated.