9.15 static Class Members

There is an important exception to the rule that each object of a class has its own copy of all the data members of the class. In certain cases, only one copy of a variable should be shared by all objects of a class. A static data member is used for these and other reasons. Such a variable represents “classwide” information, i.e., data that is shared by all instances and is not specific to any one object of the class. Recall, for example, that the GradeBook classes in Chapter 7 use static data members to store constants representing the number of grades that all GradeBook objects can hold.

9.15.1 Motivating Classwide Data

Let’s further motivate the need for static classwide data with an example. Suppose that we have a video game with Martians and other space creatures. Each Martian tends to be brave and willing to attack other space creatures when the Martian is aware that at least five Martians are present. If fewer than five are present, each Martian becomes cowardly. So each Martian needs to know the martianCount. We could endow each object of class Martian with martianCount as a data member. If we do, every Martian will have a separate copy of the data member. Every time we create a new Martian, we’ll have to update the data member martianCount in all Martian objects. Doing this would require every Martian object to have, or have access to, handles to all other Martian objects in memory. This wastes space with the redundant copies of the martianCount and wastes time in updating the separate copies. Instead, we declare martianCount to be static. This makes martianCount classwide data. Every Martian can access martianCount as if it were a data member of the Martian, but only one copy of the static variable martianCount is maintained in the program. This saves space. We save time by having the Martian constructor increment static variable martianCount and having the Martian destructor decrement martianCount. Because there’s only one copy, we do not have to increment or decrement separate copies of martianCount for each Martian object.

Performance Tip 9.5

Use static data members to save storage when a single copy of the data for all objects of a class will suffice—such as a constant that can be shared by all objects of the class.

9.15.2 Scope and Initialization of static Data Members

A class’s static data members have class scope. A static data member must be initialized exactly once. Fundamental-type static data members are initialized by default to 0. Prior to C++11, a static const data member of int or enum type could be initialized in its declaration in the class definition—all other static const data members had to be defined and intialized at global namespace scope (i.e., outside the body of the class definition). In C++11, all static const data members can have in-class initializers. If a static data member is an object of a class that provides a default constructor, the static data member need not be initialized because its default constructor will be called.

9.15.3 Accessing static Data Members

A class’s private (and protected; Chapter 11) static members are normally accessed through the class’s public member functions or friends. A class’s static members exist even when no objects of that class exist. To access a public static class member when no objects of the class exist, simply prefix the class name and the scope resolution operator (::) to the name of the data member. For example, if our preceding variable martianCount is public, it can be accessed with the expression Martian::martianCount, even when there are no Martian objects. (Of course, using public data is discouraged.)

To access a private or protected static class member when no objects of the class exist, provide a public static member function and call the function by prefixing its name with the class name and scope resolution operator. A static member function is a service of the class, not of a specific object of the class.

Software Engineering Observation 9.12

A class’s static data members and static member functions exist and can be used even if no objects of that class have been instantiated.

9.15.4 Demonstrating static Data Members

The program of Figs. 9.289.30 demonstrates a private static data member called count (Fig. 9.28, line 23) and a public static member function called getCount (Fig. 9.28, line 17). In Fig. 9.29, line 8 defines and initializes the data member count to zero at global namespace scope and line 12 defines static member function getCount. Notice that neither line 8 nor line 12 includes keyword static, yet both lines define static class members. The static keyword cannot be applied to a member definition that appears outside the class definition. Data member count maintains a count of the number of objects of class Employee that have been instantiated. When objects of class Employee exist, member count can be referenced through any member function of an Employee object—in Fig. 9.29, count is referenced by both line 18 in the constructor and line 27 in the destructor.

Fig. 9.28 Employee class definition with a static data member to track the number of Employee objects in memory.

Alternate View

 1   // Fig. 9.28: Employee.h
 2   // Employee class definition with a static data member to
 3   // track the number of Employee objects in memory
 4   #ifndef EMPLOYEE_H
 5   #define EMPLOYEE_H
 6
 7   #include <string>
 8
 9   class Employee {
10   public:
11      Employee(const std::string&, const std::string&); // constructor
12      ~Employee(); // destructor
13      std::string getFirstName() const; // return first name
14      std::string getLastName() const; // return last name
15
16      // static member function                                          
17      static unsigned int getCount(); // return # of objects instantiated
18   private:
19      std::string firstName;
20      std::string lastName;
21
22      // static data
23      static unsigned int count; // number of objects instantiated
24   };
25
26   #endif

Fig. 9.29 Employee class member-function definitions.

Alternate View

 1   // Fig. 9.29: Employee.cpp
 2   // Employee class member-function definitions.
 3   #include <iostream>
 4   #include "Employee.h" // Employee class definition
 5   using namespace std;
 6
 7   // define and initialize static data member at global namespace scope
 8   unsigned int Employee::count{0}; // cannot include keyword static    
 9
10   // define static member function that returns number of         
11   // Employee objects instantiated (declared static in Employee.h)
12   unsigned int Employee::getCount() {return count;}               
13
14   // constructor initializes non-static data members and
15   // increments static data member count
16   Employee::Employee(const string& first, const string& last)
17      : firstName(first), lastName(last) {
18      ++count; // increment static count of employees
19      cout << "Employee constructor for " << firstName
20         << ' ' << lastName << " called." << endl;
21   }
22
23   // destructor decrements the count
24   Employee::~Employee() {
25      cout << "~Employee() called for " << firstName
26         << ' ' << lastName << endl;
27      --count; // decrement static count of employees
28   }
29
30  // return first name of employee
31  string Employee::getFirstName() const {return firstName;}
32
33  // return last name of employee
34  string Employee::getLastName() const {return lastName;}

Figure 9.30 uses static member function getCount to determine the number of Employee objects in memory at various points in the program. The program calls Employee::getCount() before any Employee objects have been created (line 11), after two Employee objects have been created (line 22) and after those Employee objects have been destroyed (line 33). Lines 15–28 in main define a nested scope. Recall that local variables exist until the scope in which they’re defined terminates. In this example, we create two Employee objects in the nested scope (lines 16–17). As each constructor executes, it increments class Employee’s static data member count. These Employee objects are destroyed when the program reaches line 28. At that point, each object’s destructor executes and decrements class Employee’s static data member count.

Fig. 9.30 static data member tracking the number of objects of a class.

Alternate View

 1   // Fig. 9.30: fig09_30.cpp
 2   // static data member tracking the number of objects of a class.
 3   #include <iostream>
 4   #include "Employee.h" // Employee class definition
 5   using namespace std;
 6
 7   int main() {
 8      // no objects exist; use class name and binary scope resolution
 9      // operator to access static member function getCount
10      cout << "Number of employees before instantiation of any objects is "
11         << Employee::getCount() << endl; // use class name
12
13      // the following scope creates and destroys
14      // Employee objects before main terminates
15      {
16         Employee e1{"Susan", "Baker"};
17         Employee e2{"Robert", "Jones"};
18
19      // two objects exist; call static member function getCount again
20      // using the class name and the scope resolution operator
21      cout << "Number of employees after objects are instantiated is "
22         << Employee::getCount();
23
24      cout << "

Employee 1: "
25         << e1.getFirstName() << " " << e1.getLastName()
26         << "
Employee 2: "
27         << e2.getFirstName() << " " << e2.getLastName() << "

";
28      }
29
30      // no objects exist, so call static member function getCount again
31      // using the class name and the scope resolution operator
32      cout << "
Number of employees after objects are deleted is "
33         << Employee::getCount() << endl;
34      }

Number of employees before instantiation of any objects is 0
Employee constructor for Susan Baker called.
Employee constructor for Robert Jones called.
Number of employees after objects are instantiated is 2

Employee 1: Susan Baker
Employee 2: Robert Jones

~Employee() called for Robert Jones
~Employee() called for Susan Baker

Number of employees after objects are deleted is 0

A member function should be declared static if it does not access non-static data members or non-static member functions of the class. Unlike non-static member functions, a static member function does not have a this pointer, because static data members and static member functions exist independently of any objects of a class. The this pointer must refer to a specific object of the class, and when a static member function is called, there might not be any objects of its class in memory.

Common Programming Error 9.6

Using the this pointer in a static member function is a compilation error.

 

Common Programming Error 9.7

Declaring a static member function const is a compilation error. The const qualifier indicates that a function cannot modify the contents of the object on which it operates, but static member functions exist and operate independently of any objects of the class.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset