Section 1 - Static Members
Introduction to Week Seven
This week we will complete our introduction to classes and OOP by exploring static members, discussing member objects and introducing the this keyword (among other things). You will start to think like a professional OOP programmer in the next few days.
Reading
The optional textbook reading is handled as we have been doing all along: look up the terms with which you want more coverage in the index and see where it leads.
7A.1.1 Instance and Class Data
We said that a static class member is data defined in the class (but outside the method definitions) that is qualified with the static keyword. Instance members are those that do not have the static keyword. Let's learn the difference between these two kinds of member data.
Since a class is an abstract blueprint for a hand-rolled data type, we know that nothing really happens until we declare (i.e., instantiate) one or more objects of that class. Each time we instantiate an object, a new set of instance members is associated with that object, distinct from the instance members of other objects of the class. If you have two Galaxy objects, you have two separate sets of data, one for each object.
In contrast, with static class members, there is only one datum associated with the entire class. If a member of a class is qualified by the static keyword, then no matter how many objects we instantiate, there is only one variable for that member, and it is shared by all the objects of the class.
Why would we want such a communistic variable to be in our class? There could be many reasons. Here is an example of just one:
class Dog { public: static long population; private: long licenseNumber; // other members ... }; // later on in the client or main method ... Dog fido, lucy, watson;
We have declared three new Dog objects - in other words, we instantiated 3 dogs. fido, lucy and watson each has its own personal copy of the licenseNumber data because licenseNumber is an instance member. However, they all share the one and only static member, population. This might be useful if, for instance, we wanted the population to be incremented each time a Dog was born, and decremented each time a Dog died. It is a value that is common to all dogs in the Dog class. But the licenseNumber is unique to each instance.
Exploring this a little further, we look back to our Employee example at the line:
walter.age = 61;
which clearly makes sense to us. It is saying that the age field (I'll switch terminology to make sure you get used to both terms: member and field) in the walter instance is being set to 61. Similarly with a Dog:
watson.licenseNumber = 999332222;
is how to modify watson's own licenseNumber. But what does:
watson.population++;
do? Well it clearly increments population, but since population is a static class member, there is only one such field for the entire class, and we are modifying that field. Since there is no more reason to access this population field through use of the watson reference than there would be to access it through the fido or lucy reference, we are offered a preferred method of access to static class members, namely, through the class name:
Dog::population++;
Here we see more clearly what we are doing. Usually, you will want to use the class name when dereferencing a static class field.
7A.1.2 Initializing Static Member Data
As you know, we have a special method whose job it is to initialize all the instance member data for us. That method is called a constructor, and it always has the same name as the name of the class. This guarantees that whenever we instantiate an object of that class, its member data are all initialized to something reasonable.
Galaxy gal1, gal2;
These two objects now have their private data set because of the Galaxy constructor (go back and see what that constructor did, if you forgot). Also, you may have followed my advice and created a constructor for the Employee class in the previous section.
Now that we have this concept of a static member, however, we have to figure out how to initialize it. Imagine the population member outlined above. We want to initialize it to 0. Fine. Since it is a public member, we could have our client do it with a simple assignment statement, Dog::population = 0; But we should really make population private. Private instance data is usually initialized in the constructor, but we can't use a constructor for private static data: If, each time a Dog was born, the member population were set to 0, we would always have a population of 0!. Meanwhile, we need an alternative. C++ gives us a somewhat awkward way to initialize static class members. After the class prototype, you would dereference the static member with the class name and initialize it like so:
class Dog { private: static long population; // private static member long licenseNumber; // private instance member }; // initialize static member long Dog::population = 0;
When the program starts, population will be set to 0. That assignment is not visited ever again, so population can be adjusted as Dogs are created or destroyed.
7A.1.3 What Constructors Can Do With Static Members
The last section told us that constructors are not meant to initialize static class data, and gave us an alternative way to do that. But does that mean constructors should ignore static members? Not necessarily. In the example with the Dog population, whenever an object is created we'd like to count it as part of the population. That's a simple matter of adding one to the member population. Here is how it would look:
Dog::Dog() { population++; // etc. ... }
So every time a Dog is born, its private data would be initialized to 0 or whatever makes sense. Then, we would add one to population. Later, when we learn about destructor functions, we'll see that's a good place to subtract one from the population variable.
There might be other kinds of static member data, and these would be treated in their own unique way.