We're always interested in getting feedback. E-mail us if you like this guide, if you think that important material is omitted, if you encounter errors in the code examples or in the documentation, if you find any typos, or generally just if you feel like e-mailing. Send your email to Frank Brokken.Please state the document version you're referring to, as found in the title (in this document: 5.2.0a) and please state the paragraph you're referring to.
All mail received is seriously considered, and new (sub)releases of the Annotations will normally reflect your suggestions for improvements. Except for the incidental case I will not otherwise acknowledge the receipt of suggestions for improvements. Please don't misinterpret this for lack of appreciation.
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 could access the members of objects of that class.
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 would perform the necessary initialization and would 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++'s solution is to define
static members
: data and functions, common
to all objects of a class and inaccessible outside of the class. These
functions and data will be discussed in this chapter.
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 again and again, for each separate object of the class.
Static data members are created when the program
starts executing. Note, however, that they are always created as members of
their classes. It is suggested to prefix static member names with s_
in
order to distinguish them (in class member functions) from the class' data
members.
Static data members
which are declared
public
are like `normal'
global variables: they can be reached by
all code of the program by simply using their names, together with their
class names and the scope resolution operator. This is illustrated in the
following example:
class Test { public: static int s_public_int; private: static int s_private_int; }; int main() { Test::s_public_int = 145; // ok Test::s_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 interface, and not the implementation of
static
data members, which is discussed next.
static
data member which is a private
variable in a class, consider the following example:
class Directory { static char s_path[]; public: // constructors, destructors, etc. (not shown) };The data member
path[]
is a
private static data member. During
the execution of the program, only one Directory::path[]
exists, 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 by 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 any constructor of the class has been
called. 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,
preferably in a source file dedicated to the definition of static data
members, called
data.cc
.
The data member path[]
from the above class Directory
could thus be
defined and initialized as follows in a file data.cc
:
include "directory.ih" char Directory::s_path[200] = "/usr/local";In the class interface the
static
member is actually only
declared. At its implementation (definition) its type and class name are
explicitly stated. Note also that the
size specification can be left out of
the interface, as is shown in the above array path[]
. However, its size
is (either explicitly or implicitly) needed at its definition.
Note that any source file could contain the definition of the static
data members of a class. A separate data.cc
source is advised, but the
source file containing, e.g., main()
could be used as well. Of course, any
source file definining static data of a class must also include the header
file of that class, in order for the static data member to be known to the
compiler.
A second example of a useful private static
data member is given
below. Assume that a class Graphics
defines the communication of a program
with a graphics-capable device (e.g., a VGA screen). The initialization 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 interface for this
Graphics
class might be:
class Graphics { static int s_nobjects; // counts # of objects public: Graphics(); ~Graphics(); // other members not shown. private: void setgraphicsmode(); // switch to graphics mode void settextmode(); // switch to text-mode }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. At the destruction of the last Graphics
object, the
switch from graphics mode to text mode is made:
int Graphics::s_nobjects = 0; // the static data member Graphics::Graphics() { if (!s_nobjects++) setgraphicsmode(); } Graphics::~Graphics() { if (!--s_nobjects) settextmode(); }Obviously, when the class
Graphics
would define more than one
constructor, each constructor would need to increase the variable nobjects
and would possibly have to initialize the graphics mode.
public
section of a class,
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 10.1 would be declared in the public
section of
the class definition, all program code could access this variable:
int main() { getcwd(Directory::s_path, 199); }Note that the variable
s_path
would still have to be defined. As
before, the class interface would only declare the array s_path[]
.
This means that some source file would still need to contain the definition of
the s_path[]
array.
static
data, in
which these variables are shared by all objects of the class, static
member functions exist without any associated object of their class.
Static
member functions
can access all static
members of their
class, but also the members (private
or public
) of objects of
their class if they are informed about the existence of these objects, as
in the upcoming example. Static
member functions are themselves not
associated with any object of their class. Consequently, they have not
this
pointer. In fact, a static
member function is completely
comparable to a
global function, not associated with any class. This
implies that the address of a static
member function could be used in
functions having parameters that are
pointers to (global) functions. Since static
member functions are comparable to ordinary global functions, static
member functions that are declared in the
public
section of a class
interface can be called without specifying an object of the class.
The following example illustrates these characteristics of static
member
functions:
class Directory { string d_currentPath; static char s_path[]; public: static void setpath(char const *newpath); static void preset(Directory &dir, char const *path); }; // see the text below char Directory::s_path[200] = "/usr/local"; // 1 void Directory::setpath(char const *newpath) { if (strlen(newpath) >= 200) throw "newpath too long"; strcpy(s_path, newpath); // 2 } void Directory::preset(Directory &dir, char const *newpath) { dir.d_currentPath = newpath; // 3 } int main() { Directory dir; Directory::setpath("/etc"); // 4 dir.setpath("/etc"); // 5 Directory::preset(dir, "/usr/local/bin"); // 6 dir.preset(dir, "/usr/local/bin"); // 7 }
string
or a pointer to dynamic memory could have
been used.
static
data member path[]
. Note that here only static
members
are used.
static
member function modifies a
private
data member
of an object. However, the object whose member must be modified is given to
the member function as a reference parameter.
setpath()
is called. It is a static
member, so no
object is required. But the compiler must know to which class the function
belongs, so the class is mentioned, using the
scope resolution operator.
dir
is used to tell
the compiler that we're talking about a function in the Directory
class. So, static
member functions can be called as normal member
functions.
currentPath
member of dir
is altered. As in 4, the
class and the scope resolution operator are used.
dir
is used to tell
the compiler that we're talking about a function in the Directory
class. Here in particular note that this is not using preset()
as an
ordinary member function of dir
: the function still has no
this
-pointer, so dir
must be passed as argument to inform the
static
member function preset
about the object whose currentPath
member it should modify.
In the example only public static
member functions were used. C++
also allows private static
member functions: these functions can only be
called by member functions of their class.
Finally it is noted that
static
member functions cannot be
defined as
inline
functions. Since a static
function can be used as an
ordinary function at the global level, it must have an
address. The code of
inline functions is normally directly inserted into the code of the program:
not a true function, but an
alias for a few statements. With inline
functions we have no `
function address', Consequently, inline functions
lack a basic requirement for static
member functions: they have no
address.