static member functions
You can also create static member
functions
that,
like static data members, work for the class as a whole rather than for a
particular object of a class. Instead of making a global function that lives in
and “pollutes” the global or local namespace, you bring the function
inside the class. When you create a static member function, you are
expressing an association with a particular class.
You can call a static member
function in the ordinary way, with the dot or the arrow, in association with an
object. However, it’s more typical to call a static member function
by itself, without any specific object, using the scope-resolution operator,
like
this:
//: C10:SimpleStaticMemberFunction.cpp
class X {
public:
static void f(){};
};
int main() {
X::f();
} ///:~
When you see static member functions in a
class, remember that the designer intended that function to be conceptually
associated with the class as a whole.
A static member function cannot
access ordinary data members, only static data members. It can call only
other static member functions. Normally, the address of the current
object (this) is quietly passed in when any
member function is called, but a static member has no
this, which is the reason it cannot access
ordinary members. Thus, you get the tiny increase in speed afforded by a global
function because a static member function doesn’t have the extra
overhead of passing this. At the same time you get the benefits of having
the function inside the class.
For data members, static indicates
that only one piece of storage for member data exists for all objects of a
class. This parallels the use of static to define objects inside a
function to mean that only one copy of a local variable is used for all calls of
that function.
Here’s an example showing
static data members and static member
functions used together:
//: C10:StaticMemberFunctions.cpp
class X {
int i;
static int j;
public:
X(int ii = 0) : i(ii) {
// Non-static member function can access
// static member function or data:
j = i;
}
int val() const { return i; }
static int incr() {
//! i++; // Error: static member function
// cannot access non-static member data
return ++j;
}
static int f() {
//! val(); // Error: static member function
// cannot access non-static member function
return incr(); // OK -- calls static
}
};
int X::j = 0;
int main() {
X x;
X* xp = &x;
x.f();
xp->f();
X::f(); // Only works with static members
} ///:~
Because they have no this pointer,
static member functions can neither access non-static data members
nor call non-static member functions.
Notice in main( ) that a
static member can be selected using the usual dot or arrow syntax,
associating that function with an object, but also with no object (because a
static member is associated with a class, not a particular object), using
the class name and scope resolution operator.
Here’s an interesting feature:
Because of the way initialization happens for static member objects, you
can put a static data member of the same class inside that class.
Here’s an example that allows only a single object of type Egg to
exist by making the constructor private. You can access that object, but you
can’t create any new Egg objects:
//: C10:Singleton.cpp
// Static member of same type, ensures that
// only one object of this type exists.
// Also referred to as the "singleton" pattern.
#include <iostream>
using namespace std;
class Egg {
static Egg e;
int i;
Egg(int ii) : i(ii) {}
Egg(const Egg&); // Prevent copy-construction
public:
static Egg* instance() { return &e; }
int val() const { return i; }
};
Egg Egg::e(47);
int main() {
//! Egg x(1); // Error -- can't create an Egg
// You can access the single instance:
cout << Egg::instance()->val() << endl;
} ///:~
The initialization for E happens
after the class declaration is complete, so the compiler has all the information
it needs to allocate storage and make the constructor call.
To completely prevent the creation of any
other objects, something else has been added: a second private constructor
called the
copy-constructor. At this
point in the book, you cannot know why this is necessary since the copy
constructor will not be introduced until the next chapter. However, as a sneak
preview, if you were to remove the copy-constructor defined in the example
above, you’d be able to create an Egg object like
this:
Egg e = *Egg::instance();
Egg e2(*Egg::instance());
Both of these use the copy-constructor,
so to seal off that possibility the copy-constructor is declared as private (no
definition is necessary because it never gets called). A large portion of the
next chapter is a discussion of the copy-constructor so it should become clear
to you
then.