Second, the definition declares the num_strings member as belonging to the static storage class. A
Figure 12.1. A static data member.
By the way, Listing 12.1 uses the num_strings member as a convenient means of illustrating static data members and as a device to point out potential programming problems. In general, a string class doesn’t need such a member.
Take a look at the implementation of the class methods in Listing 12.2. Notice how it handles using a pointer and a static member.
Listing 12.2. strngbad.cpp
// strngbad.cpp -- StringBad class methods
#include
#include "strngbad.h"
using std::cout;
// initializing static class member
int StringBad::num_strings = 0;
// class methods
// construct StringBad from C string
StringBad::StringBad(const char * s)
{
len = std::strlen(s); // set size
str = new char[len + 1]; // allot storage
std::strcpy(str, s); // initialize pointer
num_strings++; // set object count
cout << num_strings << ": \"" << str
<< "\" object created\n"; // For Your Information
}
StringBad::StringBad() // default constructor
{
len = 4;
str = new char[4];
std::strcpy(str, "C++"); // default string
num_strings++;
cout << num_strings << ": \"" << str
<< "\" default object created\n"; // FYI
}
StringBad::~StringBad() // necessary destructor
{
cout << "\"" << str << "\" object deleted, "; // FYI
--num_strings; // required
cout << num_strings << " left\n"; // FYI
delete [] str; // required
}
std::ostream & operator<<(std::ostream & os, const StringBad & st)
{
os << st.str;
return os;
}
First, notice the following statement from Listing 12.2:
int StringBad::num_strings = 0;
This statement initializes the static num_strings member to 0. Note that you cannot initialize a static member variable inside the class declaration. That’s because the declaration is a description of how memory is to be allocated but it doesn’t allocate memory. You allocate and initialize memory by creating an object using that format. In the case of a static class member, you initialize the static member independently, with a separate statement outside the class declaration. That’s because the static class member is stored separately rather than as part of an object. Note that the initialization statement gives the type and uses the scope operator, but it doesn’t use the static keyword.
This initialization goes in the methods file, not in the class declaration file. That’s because the class declaration is in a header file, and a program may include a header file in several other files. That would result in multiple copies of the initialization statement, which is an error.
The exception to the noninitialization of a static data member inside the class declaration (see Chapter 10, “Objects and Classes”) is if the static data member is a const of integral or enumeration type.
Note
A static data member is declared in the class declaration and is initialized in the file containing the class methods. The scope operator is used in the initialization to indicate to which class the static member belongs. However, if the static member is a const integral type or an enumeration type, it can be initialized in the class declaration itself.
Next, notice that each constructor contains the expression num_strings++. This ensures that each time a program creates a new object, the shared variable num_strings increases by one, keeping track of the total number of String objects. Also the destructor contains the expression --num_strings. Thus, the String class also keeps track of deleted objects, keeping the value of the num_strings member current.
Now look at the first constructor in Listing 12.2, which initializes a String object with a regular C string:
StringBad::StringBad(const char * s)
{
len = std::strlen(s); // set size
str = new char[len + 1]; // allot storage
std::strcpy(str, s); // initialize pointer
num_strings++; // set object count
cout << num_strings << ": \"" << str
<< "\" object created\n"; // For Your Information
}