Second, the definition declares the num_strings member as belonging to the static storage class. A static class member has a special property: A program creates only one copy of a static class variable, regardless of the number of objects created. That is, a static member is shared among all objects of that class, much as a phone number might be shared among all members of a family. If, say, you create 10 StringBad objects, there would be 10 str members and 10 len members, but just 1 shared num_strings member (see Figure 12.1). This is convenient for data that should be private to a class but that should have the same value for all class objects. The num_strings member, for example, is intended to keep track of the number of objects created.

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                     // string.h for some

#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

}

Перейти на страницу:

Все книги серии Developer's Library

Похожие книги