6.4 Replacing Inherited Methods


 

A derived class is capable of redefining, by overriding, an inherited method, replacing the inherited method with one that is specifically designed for the derived class. Because they are suited to its behavior, the derived class may want to inherit many of the base class's methods. What is considered in this section is how to selectively replace an inappropriate base class method with one appropriate to the derived class. The invocation of a method that has been replaced will result in the replacing method being invoked rather than the replaced method. The replacement is transparent to the code performing the invocation because the replacing method has the same signature (the same name and same arguments) as the method it replaces .

The substitution achieved by replacement is often referred to as the receiving object "doing the right thing". The receiving object is aware of its internal structure and, in response to an invocation, selects an appropriate method for execution. The invoker of the method is, and is best, unaware of how the receiving object determines which of its methods to execute. Of course, the receiving object is restricted to executing a method that matches the signature (name and ordered list of argument types) of the invocation.

An Example

The RestartCounter class is an example of a class in which method replacement is needed. A RestartCounter object is just like a Number object, except that the Reset method always sets the internal value back to an initial value that is given when the RestartCounter is constructed. The RestartCounter does not use the TextBox to obtain the value to which it is reset.

Below is the inheritance diagram for the RestartCounter class. This diagram implies that a RestartCounter object has the following methods:

  • ShowIn, Show, Value (from DisplayableNumber),
  • Next (from Number), and
  • Reset (from RestartCounter).

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


Inheritance Diagram for RestartCounter Class

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 Number::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 Number {
         private:
           int original;
         public:
                RestartCounter(int init=0);
           void Reset();                // overrides inherited method
               ~RestartCounter(();
        };

        RestartCounter::RestartCounter(int init) : Number(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 includes all of the (non-overridden) methods of its ancestors (Number and DisplayableNumber):

 TextBox display(Location(100,100), Shape(50,50));
  RestartCounter restart(7);
  restart.Next();               // from Number
  restart.ShowIn(display);      // from DisplayableNumber
  restart.Show();               // from DisplayableNumber
  restart.Reset();              // from RestartCounter (overrides
                                //    DisplayableNumber::Reset()

Method Lookup

When an invocation is received by an object with a layered structure (i.e., an object whose structure is defined through inheritance) a method lookup occurs to determine which method in the layered object to execute in response to the invocation. This method lookup is performed by the receiving object transparently to the invoker. The method lookup described below should be viewed only as a conceptual aid, for in C++ such a lookup does not actually take place at run-time. However, the method lookup described is conceptually correct and gives an intuitive way to understand the notion of overriding.

Method lookup is a bottom-up search; it always begins in the level corresponding to the most-derived class. For a JumpCounter object, the method lookup begins in the JumpCounter layer; for a Number object, the method lookup begins in the Number layer. If a method matching the invocation is found, it is selected for execution and the method lookup is complete. If a matching method is not found, the method lookup continues the search by moving to the layer immediately above the current layer.

The layered structure for a RestartCounter object illustrates overriding and method lookup. As shown below, a RestartCounter object has three layers: RestartCounter, Number, and DisplayableNumber. The figure shows the invocation of the methods Next(), Restart(), and Show().



 


Method Lookup in a RestartCounter Object


The invocation of Reset is simple because a matching method is immediately discovered in the first layer (RestartCounter). Thus, the invocation of Reset() on a RestartCounter object is bound to RestartCounter::Reset. This illustrates the effect of overriding because the method lookup will always find the Reset in the RestartCounter layer as opposed to the Reset method in the DisplayableNumber level. The invocation of Next is bound to the method Number::Next because the method lookup:

  • begins at the RestartCounter layer but did not find a matching method, and
  • continues at the next higher layer (i.e., the Number layer), where the lookup finds the matching method Number::Next

Finally, the method lookup for the invocation Show() is:

  • unsuccessful at the RestartCounter layer,
  • unsuccessful at the Number layer, and
  • successful at the DisplayableNumber layer.

Thus, the invocation of Show on a RestartCounter object is bound to DisplayableNumber::Show.

  1. Write a set of trivialized classes that have the same interfaces as the DisplayableNumber, Number, and RestartCounter classes. The methods in these trivialized classes should simply output messages indicating which method was executed. Using these trivialized classes, write a test program that creates a RestartCounter object and invokes the Next, Reset, and Show methods on it. Use the output of the program to confirm the method bindings given in this section.

  2. Write a set of trivialized classes that have the same interfaces as the DisplayableNumber, Number, and JumpCounter classes. The methods in these trivialized classes should simply output messages indicating which method was executed. Using these trivialized classes, write a test program that creates a JumpCounter object and invokes the Next and Next(10) methods on it. Explain the output of the program.

  3. Implement and test a class CheckpointCounter that is derived from the Number class. A CheckpointCounter object is like a Number, except that the value to which the Number is set by the Reset operation is determined by a Checkpoint method. The Checkpoint method saves the value of the CheckpointCounter when it is called. It is to this saved value that the CheckpointCounter is reset. Note that the CheckpointCounter will need to introduce additional data and will also need to override the Restart method in the DisplayableNumber class.

 




©1998 Prentice-Hall, Inc.
A Simon & Schuster Company
Upper Saddle River, New Jersey 07458

Legal Statement