5.4 Overriding Inherited Methods

A derived class has the ability to redefine, or override, an inherited method, replacing the inherited method by one that is specifically designed for the derived class. The derived class may want to inherit many of the base class's methods because these methods are suited to the behavior of the derived class. What is is cnsidered here is how to selectively replace an inappropriate base class method with one appropriate for the derived class.

An example of a class where overriding is needed is a RestartCounter class. A RestartCounter is just like a counter except that the Reset method always sets the internal value back to its initial value and does not use the TextBox to obtain the value to which the RestartCounter is reset.

The inheritance diagram for the RestartCounter class is shown below. This diagram implies that an RestartCounter object has the following methods:

The Reset method defined in DisplayableNumber has been overridden by the Reset method defined in RestartCounter.



Notice the difference between overloading and overriding. Overloaded methods have the same name but different argument lists (signatures). Overridden methods have the same name and the same arguments (signatures). In the JumpCounter class the Next method is overloaded because the two methods JumpCounter::Next(int) and Counter::Next() have different signatures. Thus, a JumpCounter object has both of the Next methods. However, a RestartCounter object has only one Reset method - the one defined in the RestartCounter class (RestartCounter::Reset()).

The definition of the RestartCounter class and its code is:


	class RestartCounter : public Counter {
	 private:
	   int original;
	 public:
                RestartCounter(int init=0);
           void Reset();		// overrides inherited method
	       ~RestartCounter(();
	};

        RestartCounter::RestartCounter(int init) : Counter(init) { 
		original = init; }

	void RestartCounter::Reset() { value = original; }

	 RestartCounter::~RestartCounter() {}
	
To emphasize again the role of inheritance, the following code illustrates that a RestartCounter object has all of the (non-overidden) methods of its ancestors (Counter and DisplayableNumber):
  TextBox display(Location(100,100), Shape(50,50));
  RestatCounter restart(7);
  restart.Next();		// from Counter
  restart.ShowIn(display);	// from DisplayableNumber
  restart.Show();		// from DisplayableNumber
  restart.Reset();		// from RestartCounter (overrides
				//    DisplayableNumber::Reset()

The implementation of an overriding method may use the overridden method. In this sense the overriding method extends in some way the original, inherited method. An example of where this is useful is in the Cycler class. As the class was defined earlier, it inherits a Reset method from the DisplayableNumber class. However, this inherited method allows any number to be entered by the user - possibly one outside the range of the Cycler. If this occurred, the program could access this erroneous value (via the Value method). To prevent this problem from occurring a safer (more restricted) Reset method is needed in the Cycler class. To override and extend the inherited Reset method, the Cycler class would be redefined as follows:


	class Cycler : public DisplayableNumber {
	private:
	  int base;
	public:
	  Cycler(int b, int init = 0);
	  void Next();
          void Reset();		// overrides and extends the Reset
				// method inherited from DisplayableNumber
	 ~Cycler();
	};

and the Cycler::Reset method would be implemented as follows:
	void Cycler::Reset() {
		DisplayableNumber::Reset(); // invoke overridden method
		value = value % base;
	}
In this method the overriden base class method is itself invoked. To distinguish between the two Reset methods, the syntax "DisplayableNumber::Reset" is used in the Cycler's Reset method to name the Reset method in the base class (DisplayableNumber). Without this additional syntax to specify the base class name, it would be assumed that the Cycler's Reset method was making a recursive call on itself. This is clearly not what is desired here.

Exercises

  1. Implement and test a class AutoCounter. An AutoCounter object is like a Counter object except that the Next method in the AutoCounter object does both the incrementing of the value and the update of the TextBox display. Use the technique shown above to override and extend the Counter class's Next method in the AutoCounter class.

  2. Reimplement and test the BatchCounter class. In this case BatchCounter inherits from the Counter class. However, the BatchCounter class now overrides and extends the Counter class's Next method.

  3. Reimplement and test the BatchCounter class. In this case the BatchCounter class has (as a private data member) a Cycler object. The Cycler object is used to keep track of the number of Next operations that have been done in the current "batch" of Next operations.