Last time, we started to work with objects, and saw how to make circles, then saw how to make a counter. Today, we continue to work with and improve upon the timer class.

counter1.cpp is the first example. Note that the timer class must be initialized with the initialize function. WIBNIF we could initialize it as we define it, as we can for other types? (e.g.,
int n = 5;) Well, you'll all be overjoyed to hear that you can, in fact, do just that.

Constructor Functions

counter2.cpp introduces the use of constructor functions. The proper syntax for their use is:

Constructor functions can be overloaded, by use of different numbers of parameters. counter3.cpp demonstrates how these can be used. There are two constructors, one with a limit and value, and one that just takes the limit and sets the value to 0. This file also has a reset function, which sets the counter's value to the value of the parameter. Look at line 102, where the counter is changed from one with a limit of 12 to one with a limit of 4 and an initial value of 2. The function is called by
c = counter (4, 2);
rather than
counter c(4, 2);
as it was called before. If the same format had been used, the compiler would have complained that we were redeclaring c within the scope, just as if we had written

{
  int n = 5;
  ...
  int n = 8;
}

More on Constructors

Using Classes to Define other Classes

Objects of one class can contain objects of another class. For example, the counter class can be used as part of a larger timer class, containing two counters, as in timer1.cpp. Notice that there are many constructors here for the counter class, including a default constructor. The private data for a counter is now an upper limit, a lower limit, and a value. The member function counter::tick has changed slightly. It now returns a 1 if the counter wrapped around, so we know when to increment the next timer. (The return value for the minutes counter's call to tick is ignored, but that's OK. If we wanted to add in hours as well, we'd use the return value for the minutes counter's call to tick then, and in turn ignore the hour's return value for tick.) The timer class has two counters as private data, one called minutes, and one called seconds. Note that both classes have tick() and show() functions. We won't get confused between them because the function always has an object attached to it. if that object is a counter, then counter::tick() is executed, and if it's a timer, then timer::tick() is executed.

Constructors when Classes Contain Objects

Actually, if you have default constructors defined, this is a cinch. Check out how easy it is:

timer::timer(int hi, int mins, int secs)
{
    minutes = counter(hi, 0, mins);
    seconds = counter(60, 0, secs);
}

So really all you're doing here is using the non-default constructors to change the value of the two counters. The default constructor was already called to create the counters when the compiler noticed that timer contained two counters. Why? Because we didn't give them any parameters when we declared them in the class definition. The net result of these two constructors is just the same as if we had only called the constructor with parameters, because the default constructor does nothing, by definition. This does not work without default constructors. If the default constructor and its definition are commented out (try this if you like) we get compile-time errors.

There is a way to do this without using default constructors, but it's really ugly. For the truly curious, timer2.cpp showcases this syntax. This method is limited in terms of what you can pass as parameters to the constructors. Only simple calculations are permitted. It will not be used in this course. (I really haven't seen it too much after this course, either -- Glen.)

Friends and Overloading Operators

Sometimes you want to grant access to private members to member functions of another class, or to functions not in any class at all. Declare these as friends in the class that is granting that access. Remember:

A friend can see your private parts.

You can use this same technique to overload operators, (That's the way cout << and cin >> work. They're overloaded bit-shifting operators.) These operators will need to see your object's private members, so they must be made friends. An example of how these would be used: WIBNIF we could just say ++c; instead of c.tick();? After all, we are incrementing the timer. Well, we can, and have, as demonstrated in timer3.cpp. The gist of the method to get this to work is

  1. In the class definition:
    friend counter operator ++(counter& c);
  2. Later on:
    counter operator ++(counter& c)
    {
      if (++c.value == c.upper) // Increment.  Has it hit the upper limit?
        c.value = c.lower;      // Wrap to lower limit if it has
      return c;
    }
  3. And to call it (in this case, for the minutes counter within timer):
    ++t.minutes;

Before you close up the file, look at the use of the increment operator in the definition of ++timer in line 143. The class counter now uses a separate function to return whether it has wrapped or not. If seconds wraps, then minutes is incremented. This just wouldn't look nearly as cool without operator overloading.

To Index Previous Next