5.3 Inheriting Operations and Data


In C++, the relationship between a base and derived classes is represented syntacticaly as follows:

	class DisplayableNumber {...};			// base class
	class Counter : public DisplayableNumber {...};	// derived class
	class Cycler  : public DisplayableNumber {...};	// derived class

The additional syntax " : public DisplayableNumber" is used in the definition of each derived class to name the base class from which the derived class inherits. The keyword "public" in this context means that all of the public methods defined in the base class (DisplayableNumber) become part of the public interface of the derived class (Counter, Cycler). These inherited methods represent the code sharing that is achieved by inheritance.

The public interface of the three classes DisplayableNumber, Counter and Cycler are as follows:

	class DisplayableNumber {
	public:
	  DisplayableNumber(int init = 0);  // initial value zero by default
	  void ShowIn(TextBox& p);
	  void Show();
	  void Reset();
	  int  Value();
	 ~DisplayableNumber();
	};

	class Counter : public DisplayableNumber {
	public:
	  Counter(int init = 0);
	  void Next();
	 ~Counter();
	};

	class Cycler : public DisplayableNumber {
	public:
	  Cycler(int b, int init = 0);
	  void Next();
	 ~Cycler();
	};

The critical point is that, through inheritance, Counter and Cycler objects have not only those methods defined in the Counter and Cycler classes, but also those methods inherited from the public interface of the base class, DisplayableNumber. For example:

	Counter count(10);	// initially 10
        Cycler  binary(2);	// base 2

	TextBox display(Location(10,10), Shape(50,50));
        TextBox onoff  (Location(20,20), Shape(50,50));

	count.ShowIn(display);
        binary.ShowIn(onoff);

        count.Next();		// increment by 1
        binary.Next();		// increment by 1 modulo 2

        count.Show();		// display updated value
	binary.Show();		// display updated value

        int c = count.Value();	// get value of Counter object
        int b = binary.Value(); // get value of Cycler object

As shown in this example, the Counter and Cycler objects can respond both to the methods defined in their immediate classes (Next) and also to the inherited methods (ShowIn, Show, Reset and Value) that are defined in the base class DisplayableNumber.

Data can also be placed in the base class. In the example above, the value and textBox variables are common to both the Counter and Cycler classes. This data should be promoted to the base class. The variable textBox is used only in the methods ShowIn, Show and Reset, all of which are in the base class (DisplayableNumber). Thus, the textBox variable can be placed in the private data area of the base class. The variable value, however, is needed in both the base class (where it is used by the Show and Reset methods) and in the derived class (where it is used in the Next method).

Data to be shared between the base and derived classes is placed in a new region, the "protected" regions, in the base class. Data placed in this new region is accessible both to the base class and to the derived classes. Similar to the "private:" and "public:" regions, the new region is introduced by the keyword "protected:".

The protected region is needed because neither the private nor public regions are adequate places to put the shared data. If the shared data is placed in the private region of the base class the data is inaccessible to the derived classes. This is, of course, contrary to what is needed. If the shared data is placed in the public region of the base class then it is accessible to the derived classes but it is also accessible as public data in the interface of the derived class. This means that the data looses its encapsulation and is exposed to the user of the objects created from the derived class. This is also contrary to what is needed.

The protected region contains data (and operations) that are accessible to the base class, accessible to the derived class, and inaccessible everywhere else. Protected data (and operations) are not part of the public interface of either the base class or the derived classes.

The class definition, including the data private and protected data, for the DisplayableNumber base class is:

   class DisplayableNumber {
    private:
      TextBox* textBox;			// place to display value

    protected:		// the following are accessible in derived classes
      int      value;			// internal counter 

    public:
	   DisplayableNumber(int init);  // initial value 
      void ShowIn(TextBox& p);
      void Show();
      void Reset();
      int  Value();			// reply current value
	  ~DisplayableNumber();
   };

Notice that the base class DisplayableNumber forms a complete class - it defines an unchanging integer number that can be dispalyed in a TextBox. In some limited cases this may be all that is needed. For example, a portion of an interactive testing system may need to display a final score to the user as follows:
	TextBox scoreBox(Location(100,100), Shape(75, 50));
	int score;
	
	// interact with user; determine final score

	DisplayableNumber finalScore(score);
	finalScore.ShowIn(scoreBox);
	finalScore.Show();

The classes derived from DisplayableNumber introduce the ability to change the number dynamically. Each new derived class embodies a different way to change the value.

The constructors for derived classes, like the Counter and Cycler classes, must be related to the constructors of their base classes, like DisplayableNumber. The constructor for the Counter class has an integer argument that should be used to initialize the Counter's internal "value". However, the DisplayableNumber class also has a constructor for that purpose. In this case, the Counter's constructor argument should simply be passed on to the DisplayableNumber's constructor. The C++ syntax for this is:

	Counter::Counter(int init) : DisplayableNumber(init) { }

The additional syntax ": DisplayableNumber(init) " means that the derived class constructor argument (init) is used as the base class constructor argument. In this case, no other initialization is done in the derived class (the body of the constructor has no code).

The Cycler class illustrates a different relationship between the constructors of the derived and base classes. The first integer value for the Cycler's constructor is the base of the Cycler and the second integer value is the initial value of the Cycler. This is reflected in the Cycler's constructor as follows:

	Cycler::Cycler(int b, int init) : DisplayableNumber(init) { base = b; }

In this case some of the derived class's constructor arguments are used to construct the base class while otheres are used to initialize the data in the derived class itself.

Having declared the protected data in the base class, the code for the derived classes is:

	class Counter : public DisplayableNumber {
	public:
	  Counter(int init = 0);
	  void Next();
	 ~Counter();
	};

	class Cycler : public DisplayableNumber {
        private:
	  int base;
	public:
	  Cycler(int b, int init = 0);
	  void Next();
	 ~Cycler();
	};

and the code for the methods of these classes is:

	Counter::Counter(int init) : DisplayableNumber(init) { }
        void Counter::Next() { value = value + 1; }
        Counter::~Counter() {}


	Cycler::Cycler(int b, int init) : DisplayableNumber(init) { base = b; }
        void Cycler::Next() { value = (value + 1)%base; }
        Cycler::~Cycler() {}

Exercises

  1. Implement and test the DisplayableNumber base class, the Counter derived class and the Cycler derived class. Use the existing Counter class as a starting point for your development. Be aware that the previous Counter class must be replaced by the new Counter class to avoid having duplicate and different classes with the same name.

  2. Implement and test the DownCounter class. The test program should show a DownCounter object's value in a TextBox and have a button labelled "Next" that, when pressed, causes the object's Next operation to be executed. The GenericButton and ButtonActions from Project 1 can be used.

  3. Implement and test the UpDownCounter class. The test program should show a UpDownCounter object's value in a TextBox and have buttons labelled "Next" and "Previous" that, when pressed, causes the object's Next and Previous operations, respectively, to be executed. The GenericButton and ButtonActions from Project 1 can be used.

  4. Implement and test the BiCyler class. The test program should show a BiCyler object's value in a TextBox and have buttons labelled "Next" and "Previous" that, when pressed, causes the object's Next and Previous operations, respectively, to be executed. The GenericButton and ButtonActions from Project 1 can be used.

  5. Implement and test the SwitchCounter class. The test program should show a SwitchCounter object's value in a TextBox and have buttons labelled "Next" and "Switch" that, when pressed, causes the object's Next and Switch operations, respectively, to be executed. The GenericButton and ButtonActions from Project 1 can be used.

  6. Implement and test the BatchCounter class. The test program should show a BatchCounter object's value in a TextBox and have a button labelled "Next" that, when pressed, causes the object's Next operation to be executed. The GenericButton and ButtonActions from Project 1 can be used.