11.3 STATIC MEMBERS IN C


11.3 STATIC MEMBERS IN C++

As mentioned earlier in Chapter 3, a static member of a class is global to all objects of the class. Applications often require that all objects of a given type share a piece of information among themselves. Such information, whether in the form of a variable or in the form of a function, can be declared to be static. A data member or a member function of a class is declared static by using the static modifier in the declaration. A static data member is also sometimes called a class variable. A static member does not belong to any particular object of that class. There is exactly one copy per class of such a member, as opposed to one copy per object for nonstatic members.

In this section, we will illustrate different aspects of static class members via different examples.

Consider the following C++ class that has data members of type int, static int, and static const int. It has an in-class initialization for one of the two data members of type static const. Note that, in general, C++ does not permit in-class initialization of the data members of a class. As mentioned in Chapter 7, the only exception to this rule is for data members of type static const.

 
//Static1.cc #include <iostream> using namespace std; class X { public: int m; static int n; static const int p; static const int q = 51; X( int mm ) { m = mm; } }; int X::n = 100; //(A) const int X::p = 101; //(B) int main() { cout << X::n << endl; // 100 //(C) X xobj_1( 20 ); cout << xobj_1.m << " " << xobj_1.n << endl; // 20 100 //(D) X xobj_2( 40 ); cout << xobj_2.m << " " << xobj_2.n << endl; // 40 100 X::n = 1000; //(E) cout << xobj_1.m << " " << xobj_1.n << endl; // 20 1000 cout << xobj_2.m << " " << xobj_2.n << endl; // 40 1000 return 0; }

Note especially the syntax for the initialization of the static members n and p in lines (A) and (B). Even though the class definition declares that these variables are of type int and const int, those facts have to be reasserted in the initialization syntax in lines (A) and (B).

A common way to retrieve the value of a static data member that is public is through the classname::static_data_member syntax, as we show in line (C) above. However, a static data member may also be retrieved through the individual objects of the class, as in line (D) above.

From the output of this program shown in the commented out line endings, it is clear that changing the value of the static member n in line (E) changes the value of n as seen through the previously constructed objects xobj_1 and xobj_2. This should reinforce what we said at the beginning of this section: There exists only one copy of a static data member for all the objects made from a class. If the value of the static member is changed, that change will become evident to all the objects of that class, even objects constructed prior to the change in the value of the static data member.

The syntax classname::static_data_member for accessing a static data member evidently can only be used when the data member is in the public section of a class. How does one initialize and modify a static data member that is in the private section of a class? To consider this situation, the program below defines a class X with a static data member n in the private section of the class:

 
//Static2.cc #include <iostream> using namespace std; class X { static int n; public: int m; X( int mm ) { m = mm; } static int getn() { return n; } //(A) static void setn( int nn ) { n = nn; } //(B) }; int X::n = 100; //(C) int main() { cout << X::getn() << endl; // 100 //(D) X xobj_1(20); cout << xobj_1.m << " " << xobj_1.getn() << endl; // 20 100 X xobj_2(40); cout << xobj_2.m << " " << xobj_2.getn() << endl; // 40 100 X::setn( 1000 ); //(E) cout << xobj_1.m << " " << xobj_1.getn() << endl; // 20 1000 //(F) cout << xobj_2.m << " " << xobj_2.getn() << endl; // 40 1000 return 0; }

Perhaps the most noteworthy feature of this program is that even though the static data member n is in the private section of the class, its initialization syntax remains unchanged, as shown in line (C) of the program.

The program also defines two public static member functions,[5] getn () and setn (), in lines (A) and (B) for retrieving and modifying the value of the private static data member. A static function can be called directly with respect to the class name using the scope operator. Therefore, as we show in line (E), to set the value of n to 1000, we can say

     X::setn(1000); 

Similarly, as we show in line (D), we can retrieve the value of n by

     cout << X::getn(); 

What's interesting is that while the static property allows the functions getn() and setn() to be invoked directly on the class, these static functions can also be invoked on the objects of the class, as in line (F) above. That is, if xobj_1 is an object of type X, we can say:

     cout << xobj_1.getn(); 

The simple class X used above, while serving as a convenient example to get across the basic idea of static members, is much too sterile to show some of the nuances associated with their use in more meaningful situations. In what follows, we will present two different examples to illustrate the different ways of initializing and updating static data members. Each example has a different problem description and the manner in which the static members are used is dictated by that description. Before we actually delve into the examples, here is what they are about:

  • Our first example, from Stroustrup [54], involves a class Date. It would be convenient if all objects of type Date had access to a single object today. This would require that today be a static data member of the Date class.

  • Our second example, patterned after a similar Java example from Arnold and Gosling [2], will deal with a robot factory. Suppose, in order to keep track of the robots manufactured by a factory, we define a class Robot with a data member idNumber whose value designates the serial number for each robot. We would need a way to keep track of the next available idNumber for the next robot to come off the manufacturing line. For that purpose, we may define a static member next IdNumber that would reside in the class itself.

Example 1

Here is an implementation of the Date class. Note the static data member today in line (A) and the static member functions setToday() and getToday() in lines (B) and (C).

 
//Date.cc #include <iostream> using namespace std; class Date { int d, m, y; static Date today; //(A) public: Date( int dd = 0, int mm = 0, int yy = 0 ); static void setToday( int, int, int ); //(B) static Date getToday() { return today; }; //(C) void print() { cout << "day: " << d << " month: " << m << " year: " << y << endl; } }; Date::Date( int dd, int mm, int yy ) { d = dd ? dd : today.d; m = mm ? mm : today.m; y = yy ? yy : today.y; } void Date::setToday( int dd, int mm, int yy ) { //(D) today = Date( dd, mm, yy ); } Date Date::today( 31, 10, 2001 ); //(E) int main() { Date d1( 1, 1, 1970 ); d1.print(); //day: 1 month: 1 year: 1970 Date d2(2); d2.print(); //day: 2 month: 10 year: 2001 Date::setToday( 3, 4, 2000 ); //(F) Date::getToday().print(); //day: 3 month: 4 year: 2000 //(G) Date d3( 7 ); d3.print(); //day: 7 month: 4 year: 2000 Date d4 = Date(); d4.print(); //day: 3 month: 4 year: 2000 return 0; }

Note the initialization of the static member today outside the class definition in line (E). Despite the fact that the static member today is private, for the purpose of initialization (and only initialization) it is accessible outside the class in the same manner as a public static member, a point that was also made earlier with the help of class X.

In lines (F) and (G), the program shows the invocations of the static member functions getToday and setToday directly on the class.

Finally, note that in line (C) the data member today is of the same type as the class itself of which it is a member. In C++, you can do this for only static data members. In other words, if today were a nonstatic member, we could not have written

     Date today;             (WRONG) 

However, we would be able to write

     Date* today_ptr; 

C++ does not permit a nonstatic member of a class T to be of type T. However, it can be of type T*. Java makes no such distinction. In other words, a data member, static or nonstatic, of Java class can be of the same type as the enclosing class.

Example 2

Lest the reader think that static data members can only be changed by static member functions, we will now show an example where that is not the case. The Robot class below has one static data member in line (A), but no static member functions at all. As we make Robot objects, we want to associate with each robot a serial number to be stored in the data member idNum. The static data member nextIdNum keeps track of the next available serial number for the robot that will be made next.

 
//Robot.cc #include <iostream> #include <string> using namespace std; class Robot { int idNum; static int nextIdNum; //(A) string owner; public: int getIdNum(); int what IsNext IdNum(); //(B) string getOwner(); Robot ( string ownername); void print() { cout << idNum << " " << owner << endl; } }; int Robot:: nextIdNum = 1; //(C) int Robot:: what IsNextIdNum () { return nextIdNum++; } int Robot::getIdNum() { return idNum; } string Robot::getOwner() { return owner; } Robot::Robot(string name) { idNum = whatIsNextIdNum(); //(D) owner = name; } int main() { Robot r1("ariel"); r1.print(); // 1 ariel Robot r2("mauriel"); r2.print(); // 2 mauriel Robot r3("mercurial"); r3.print(); // 3 mercurial return 0; }

Note the following special feature of the static member nextIdNum: Every time we use its value for an object of type Robot, the value is automatically incremented for the next robot. This is accomplished by the nonstatic method whatIsNextIdNum(), declared at (B), and by the incorporation of this method in the main constructor at (D). The static member nextIdNum is set initially to 1 by the usual initialization syntax in line (C).

11.3.1 Initialization and Destruction of Static Objects in C++

One of the more interesting aspects of C++ is that there is usually more to the main of a program than meets the eye, especially if the program includes static class members and their initializations in global scope. Consider the following program:[6]

 
//StaticInit.cc #include <iostream> using namespace std; class X { public: X() { cout << "X's constructor invoked" << endl; } ~X() { cout << "X's destructor invoked" << endl; } }; class Y { public: static X xobj; //(A) }; X Y::xobj; //(B) int main() { cout << "This is the first executable statement of main" //(C) << endl; return 0; }

As evident from line (C), all that the main of this program calls for is the printing out of the string

     This is the first executable statement of main 

But if you compile and run this program, it outputs the following three lines:

     X's constructor invoked     This is the first executable statement of main     X's destructor invoked 

Where did this additional output come from? From static initialization and static destruction, as we explain below.

When a program includes initialization of static class members in the global scope, as in line (B) above, the C++ compiler introduces additional code into main, before the first programmer-written executable statement, for the creation of the static objects. For the eventual destruction of objects created in this manner, the compiler introduces yet more code into main immediately after the last programmer-written statement. In the above program, X's constructor for the initialization in line (B) is invoked before the statement in line (C) and X's destructor after line (C), accounting for the output shown.

[5]An important property of static member functions to remember for future is: A static member function is not allowed to access the nonstatic members of a class.

[6]If the reader is not already familiar with the concept of a destructor in C++ from its earlier brief introduction in Chapter 3, this subsection is best read after the material in Section 11.8.




Programming With Objects[c] A Comparative Presentation of Object-Oriented Programming With C++ and Java
Programming with Objects: A Comparative Presentation of Object Oriented Programming with C++ and Java
ISBN: 0471268526
EAN: 2147483647
Year: 2005
Pages: 273
Authors: Avinash Kak

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net