In the previous chapters we have shown examples of classes where each object
of a class had its own set of public or private data.
Each public or private function would then access the
object's own version of the data.
In some situations it may be desirable that one or more common data fields exist, which are accessible to all objects of the class. An example of such a situation is the name of the startup directory in a program which recursively scans the directory tree of a disk. A second example is a flag variable, which states whether some specific initialization has occurred: only the first object of the class would then perform the initialization and would then set the flag to `done'.
Such situations are analogous to C code, where several functions need
to access the same variable. A common solution in C is to define all
these functions in one source file and to declare the variable as a
static: the variable name is then not known beyond the scope of the
source file. This approach is quite valid, but doesn't stroke with our
philosophy of one function per source file. Another C-solution is to give
the variable in question an unusual name, e.g., _6ULDV8, and then
to hope that other program parts won't use this name by accident. Neither the
first, nor the second C-like solution is elegant.
C++ therefore allows static data and functions, which are
common to all objects of a class. These functions and data will be discussed in
this chapter.
A data member of a class can be declared static; be it in the
public or private part of the class definition. Such a
data member is created and initialized only once; in contrast to non-static data
members, which are created for each object of the class. A static
data member is created when the program starts executing; but is nevertheless
part of the class.
static data members which are declared public are
like `normal' global variables: they can be addressed by all code of the program
by using their name, preceded by the class name and by the scope resolution
operator. This is illustrated in the following code fragment:
class Test
{
public:
static int
public_int;
private:
static int
private_int;
}
int main ()
{
Test::public_int = 145; // ok
Test::private_int = 12; // wrong, don't touch
// the private parts
return (0);
}
This code fragment is not suitable for consumption by a C++ compiler:
it only illustrates the declaration, and not the definition of
static data members. We will discuss the definition of such members
shortly.
To illustrate the usage of a static data member which is a
private variable in a class, consider the following code
fragment:
class Directory
{
public:
// constructors, destructors, etc. are
// not shown here
.
.
private:
// data members
static char path [];
};
The data member path is a private
static variable. It exists only once, even though more than one
object of the class Directory may exist. This data member could be
inspected or altered by the constructor, destructor or any other member function
of the class Directory.
Since constructors are called for each new object of a class,
static data members are never initialized by constructors;
at most they are modified. The reason for this is that the
static data members exist before the constructor of the
class is called for the very first time. The static data members
can be initialized during their definition, outside of all member functions, in
the same way as global variables are initialized. The definition and
initialization of a static data member usually occurs in one of the
source files of the class functions. The data member path from the
above class Directory could thus be defined and initialized in the
source file of the constructor:
// the static data member: definition and initialization
char
Directory::path [200] = "/usr/local";
// the default constructor
Directory::Directory ()
{
.
.
}
It should be noted that the definition of the static data member
can occur in any source file; as long as it occurs only once. During the
class definition the static member is actually only
declared; during its definition the type and class name are explicitly
stated. Note also that the size specification can be left out of the
declaration, as is the case in the above array path; it is however
needed in the definition.
A second example of a useful private static data
member is given below. A class Graphics defines the communication
of a program with a graphics-capable device (e.g., a VGA screen). The initial
preparing of the device, which in this case would be to switch from text mode to
graphics mode, is an action of the constructor and depends on a
static flag variable nobjects. The variable
nobjects simply counts the number of Graphics objects
which are present at one time. Similarly, the destructor of the class may switch
back from graphics mode to text mode when the last Graphics object
ceases to exist.
The class definition is given below:
class Graphics
{
public:
// constructor, destructor
Graphics ();
~Graphics ();
// other interface is not shown here,
// e.g. to draw lines or whatever
private:
// counter of # of objects
static int nobjects;
// hypothetical functions to switch to graphics
// mode or back to text mode
void setgraphicsmode ();
void settextmode ();
}
The purpose of the variable nobjects is to count the number of
objects which exist at one given time. When the first object is created, the
graphics device is initialized. When the last object is destroyed, the switch
from graphics mode to text mode is made:
// the static data member
int Graphics::nobjects = 0;
// the constructor
Graphics::Graphics ()
{
if (! nobjects)
setgraphicsmode ();
nobjects++;
}
// the destructor
Graphics::~Graphics ()
{
nobjects--;
if (! nobjects)
settextmode ();
}
It is obvious that when the class Graphics would define more
than one constructor, each constructor would need to increase the variable
nobjects and possibly would have to initialize the graphics
mode.
Data members can be declared in the public section of a class
definition, although this is not common practice (such a setup would violate the
principle of data hiding). E.g., when the static data member
path from section StaticData
would be declared in the public section of the class
definition, all program code could access this variable:
int main ()
{
getcwd (Directory::path, 199);
return (0);
}
Note that the variable path would still have to be defined: the
class definition would still contain only the declaration of the string
path. This means that some source file would still need to contain
the definition
char
Directory::path [200];
Besides static data, C++ allows the definition of
static functions. Similar to the concept of static
data, in which these variables are shared by all objects of the class,
static functions apply to all objects of the class.
The static functions can therefore address only the
static data of a class; non-static data are
unavailable to these functions. If non-static data could be
addressed, to which object would they belong? Similarly, static
functions cannot call non-static functions of the class. All this
is caused by the fact that static functions have no
this pointer.
Functions which are static and which are declared in the
public part of a class definition can be called without specifying
an object of the class. This is illustrated in the following code fragment:
class Directory
{
public:
// constructors, destructors etc. not shown here
.
.
// here's the static public function
static void setpath (char const *newpath);
private:
// the static string
static char path [];
};
// definition of the static variable
char Directory::path [199] = "/usr/local";
// the static function
void Directory::setpath (char const *newpath)
{
strncpy (path, newpath, 199);
}
// example of the usage
int main ()
{
// Alternative (1): calling setpath() without
// an object of the class Directory
Directory::setpath ("/etc");
// Alternative (2): with an object
Directory
dir;
dir.setpath ("/etc");
return (0);
}
In the example above the function setpath() is a
public static function. C++ also allows
private static functions: in that case, the function
can only be called from other member functions but not from `outside the class'.
Note that such a private static function could only (a) access
static variables, or (b) call other static functions: non-static code or data
members would still be inaccessible.