static
Class MembersClasses sometimes need members that are associated with the class, rather than with individual objects of the class type. For example, a bank account class might need a data member to represent the current prime interest rate. In this case, we’d want to associate the rate with the class, not with each individual object. From an efficiency standpoint, there’d be no reason for each object to store the rate. Much more importantly, if the rate changes, we’d want each object to use the new value.
static
MembersWe say a member is associated with the class by adding the keyword static
to its declaration. Like any other member, static
members can be public
or private
. The type of a static
data member can be const
, reference, array, class type, and so forth.
As an example, we’ll define a class to represent an account record at a bank:
class Account {
public:
void calculate() { amount += amount * interestRate; }
static double rate() { return interestRate; }
static void rate(double);
private:
std::string owner;
double amount;
static double interestRate;
static double initRate();
};
The static
members of a class exist outside any object. Objects do not contain data associated with static
data members. Thus, each Account
object will contain two data members—owner
and amount
. There is only one interestRate
object that will be shared by all the Account
objects.
Similarly, static
member functions are not bound to any object; they do not have a this
pointer. As a result, static
member functions may not be declared as const
, and we may not refer to this
in the body of a static
member. This restriction applies both to explicit uses of this
and to implicit uses of this
by calling a nonstatic
member.
static
MemberWe can access a static
member directly through the scope operator:
double r;
r = Account::rate(); // access a static member using the scope operator
Even though static
members are not part of the objects of its class, we can use an object, reference, or pointer of the class type to access a static
member:
Account ac1;
Account *ac2 = &ac1;
// equivalent ways to call the static member rate function
r = ac1.rate(); // through an Account object or reference
r = ac2->rate(); // through a pointer to an Account object
Member functions can use static
members directly, without the scope operator:
class Account {
public:
void calculate() { amount += amount * interestRate; }
private:
static double interestRate;
// remaining members as before
};
static
MembersAs with any other member function, we can define a static
member function inside or outside of the class body. When we define a static
member outside the class, we do not repeat the static
keyword. The keyword appears only with the declaration inside the class body:
void Account::rate(double newRate)
{
interestRate = newRate;
}
As with any class member, when we refer to a class
static
member outside the class body, we must specify the class in which the member is defined. Thestatic
keyword, however, is used only on the declaration inside the class body.
Because static
data members are not part of individual objects of the class type, they are not defined when we create objects of the class. As a result, they are not initialized by the class’ constructors. Moreover, in general, we may not initialize a static
member inside the class. Instead, we must define and initialize each static
data member outside the class body. Like any other object, a static
data member may be defined only once.
Like global objects (§ 6.1.1, p. 204), static
data members are defined outside any function. Hence, once they are defined, they continue to exist until the program completes.
We define a static
data member similarly to how we define class member functions outside the class. We name the object’s type, followed by the name of the class, the scope operator, and the member’s own name:
// define and initialize a static class member
double Account::interestRate = initRate();
This statement defines the object named interestRate
that is a static
member of class Account
and has type double
. Once the class name is seen, the remainder of the definition is in the scope of the class. As a result, we can use initRate
without qualification as the initializer for rate
. Note also that even though initRate
is private
, we can use this function to initialize interestRate
. The definition of interestRate
, like any other member definition, has access to the private
members of the class.
The best way to ensure that the object is defined exactly once is to put the definition of
static
data members in the same file that contains the definitions of the class noninline member functions.
static
Data MembersOrdinarily, class static
members may not be initialized in the class body. However, we can provide in-class initializers for static
members that have const
integral type and must do so for static
members that are constexpr
s of literal type (§ 7.5.6, p. 299). The initializers must be constant expressions. Such members are themselves constant expressions; they can be used where a constant expression is required. For example, we can use an initialized static
data member to specify the dimension of an array member:
class Account {
public:
static double rate() { return interestRate; }
static void rate(double);
private:
static constexpr int period = 30;// period is a constant expression
double daily_tbl[period];
};
If the member is used only in contexts where the compiler can substitute the member’s value, then an initialized const
or constexpr static
need not be separately defined. However, if we use the member in a context in which the value cannot be substituted, then there must be a definition for that member.
For example, if the only use we make of period
is to define the dimension of daily_tbl
, there is no need to define period
outside of Account
. However, if we omit the definition, it is possible that even seemingly trivial changes to the program might cause the program to fail to compile because of the missing definition. For example, if we pass Account::period
to a function that takes a const int&
, then period
must be defined.
If an initializer is provided inside the class, the member’s definition must not specify an initial value:
// definition of a static member with no initializer
constexpr int Account::period; // initializer provided in the class definition
Even if a
const static
data member is initialized in the class body, that member ordinarily should be defined outside the class definition.
static
Members Can Be Used in Ways Ordinary Members Can’tAs we’ve seen, static
members exist independently of any other object. As a result, they can be used in ways that would be illegal for nonstatic
data members. As one example, a static
data member can have incomplete type (§ 7.3.3, p. 278). In particular, a static
data member can have the same type as the class type of which it is a member. A nonstatic
data member is restricted to being declared as a pointer or a reference to an object of its class:
class Bar {
public:
// ...
private:
static Bar mem1; // ok: static member can have incomplete type
Bar *mem2; // ok: pointer member can have incomplete type
Bar mem3; // error: data members must have complete type
};
Another difference between static
and ordinary members is that we can use a static
member as a default argument (§ 6.5.1, p. 236):
class Screen {
public:
// bkground refers to the static member
// declared later in the class definition
Screen& clear(char = bkground);
private:
static const char bkground;
};
A nonstatic
data member may not be used as a default argument because its value is part of the object of which it is a member. Using a nonstatic
data member as a default argument provides no object from which to obtain the member’s value and so is an error.
Exercises Section 7.6
Exercise 7.56: What is a
static
class member? What are the advantages ofstatic
members? How do they differ from ordinary members?Exercise 7.58: Which, if any, of the following
static
data member declarations and definitions are errors? Explain why.// example.h
class Example {
public:
static double rate = 6.5;
static const int vecSize = 20;
static vector<double> vec(vecSize);
};
// example.C
#include "example.h"
double Example::rate;
vector<double> Example::vec;