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() {}