21.1 Derived Classes

I l @ ve RuBoard

Suppose we want a stack that allows us to push on three items at a time in addition to performing all usual operations of a stack. [1] If we parse this statement in C++ terms, we discover something significant. We want a stack that:

[1] This example is a little artificial because we wanted to keep things simple. But the techniques presented here apply to more complex objects.

  1. Does all the operations of a typical stack. (In C++ this is called a base class.)

  2. Expands on this by allowing us to do something more: specifically , push things on in groups of threes. (C++ calls this a derived class .)

Our basic stack is defined in Example 13-1.

We need to define a new expanded stack, which allows us to push multiple items. We call this an m_stack . This new stack does everything a simple stack does but also lets you push three items on at once. C++ allows you to build new classes on old ones. In this case we will be building our multiple-push stack ( m_stack ) on the existing simple stack ( stack ). Technically we will be using the class stack as a base class to create a new derived class , the multiple-push stack.

We start by telling C++ that we are creating m_stack out of stack:

 class m_stack: public stack { 

The keyword public tells C++ to make all the public members of stack accessible to the outside world. If we declared stack as private , the public and protected members of stack would be accessible only inside m_stack .

This declaration tells C++ that we are going to use stack as a base for m_stack . Figure 21-1 shows how C++ views this combination.

Figure 21-1. Derived class m_stack and base class stack
figs/c++2_2101.gif

Now we need to define the member function that pushes three items on the stack ( push_three ). The code for this function looks like:

 inline void m_stack::push_three(     const int item1,     const int item3,     const int item3) {     // This calls push in the stack class     push(item1);     push(item2);     push(item3); } 

We have been very careful in selecting the name of this member function. It is called push_three instead of push for a reason. If we called it push , the code:

 inline void m_stack::push(     const int item1,     const int item3,     const int item3) {     // This calls push in the m_stack class     push(item1);     push(item2);     push(item3); } 

would call the member function push in the class m_stack , not stack 's push as we want. The result is that we call m_stack 's push , which calls push three times. This push belongs to m_stack , so we call push again, and so on. The result is that push will call itself over and over until the system runs out of memory.

This is not want we want. We need to tell C++ that we want to call the push in stack . This can be accomplished by using the scope operator :: . The new version of m_stack::push looks like this:

 inline void m_stack::push(     const int item1,     const int item3,     const int item3) {     // This calls push in the m_stack class     stck::push(item1);     stck::push(item2);     stck::push(item3); } 

This code assumes that we need to use the name push for both the stack and m_stack classes. We don't: the name push_three is more descriptive for the m_stack member function, so we'll use that. The full definition for both the stack and m_stack classes is shown in Example 21-1.

Example 21-1. stack_c/stack_d1.cpp
 /********************************************************  * Stack                                                *  *      A file implementing a simple stack class        *  ********************************************************/ #include <cstdlib> #include <iostream> const int STACK_SIZE = 100;     // Maximum size of a stack /********************************************************  * Stack class                                          *  *                                                      *  * Member functions                                     *  *      stack -- initialize the stack.                  *  *      push -- put an item on the stack.               *  *      pop -- remove an item from the stack.           *  ********************************************************/ // The stack itself class stack {     protected:         int count;              // Number of items in the stack         int data[STACK_SIZE];   // The items themselves     public:         // Initialize the stack         stack(  );         // ~stack(  ) -- default destructor         // copy constructor defaults         // Push an item on the stack         void push(const int item);         // Pop an item from the stack         int pop(  ); }; /********************************************************  * stack::stack -- initialize the stack.                *         ********************************************************/ inline stack::stack(  ) {     count = 0;  // Zero the stack } /********************************************************  * stack::push -- push an item on the stack.            *  *                                                      *  * Warning: We do not check for overflow.               *  *                                                      *  * Parameters                                           *  *      item -- item to put in the stack                *  ********************************************************/ inline void stack::push(const int item) {     assert((count >= 0) &&            (count < sizeof(data)/sizeof(data[0])));     data[count] = item;     ++count; } /********************************************************  * stack::pop -- get an item off the stack.             *  *                                                      *  * Warning: We do not check for stack underflow.        *  *                                                      *  * Returns                                              *  *      The top item from the stack.                    *  ********************************************************/ inline int stack::pop(  ) {     // Stack goes down by one     --count;     assert((count >= 0) &&            (count < sizeof(data)/sizeof(data[0])));     // Then we return the top value     return (data[count]); } /********************************************************  * m_stack -- Stack on which we can push multiple items *  *                                                      *  * Member function                                      *  *      push_many -- push an item on the stack          *  ********************************************************/ class m_stack: public stack {     public:         // m_stack -- default constructor         // ~m_stack -- default destructor         // copy constructor defaults         // Push three items on the stack         void push_three(const int item1,                   const int item2,                   const int item3);         // Sum the elements         int sum(  ); }; /********************************************************  * m_stack::push_three -- push an item on the stack.    *  *                                                      *  * Parameters                                           *  *      item1, item2, item3 --                          *  *              items to put in the stack               *  ********************************************************/ inline void m_stack::push_three(const int item1,                  const int item2, const int item3) {     stack::push(item1);     stack::push(item2);     stack::push(item3); } /********************************************************  * m_stack::sum -- Sum the elements in the stack        *  *                                                      *  * Returns:                                             *  *      The elements in the stack.                      *  ********************************************************/ inline int m_stack::sum(  ) {     int index;          // Index into the array     int total = 0;      // Running sum     for (index = 0; index < count; ++index) {         assert(index >= 0);         assert(index < sizeof(data)/sizeof(data[0]));         total += data[index];     }     return (total); } 

You may have noticed that we've added a member function called sum . This function returns the total of all the elements in the stack. Our sum function needs access to the array named data in the class stack to work. Normally this variable would be declared private to prevent outsiders from messing with it. But in this case we would like for no one but m_stack to be able to access this variable. The C++ keyword protected gives us the access we want. It tells C++ that any derived class that uses this class as a base class can access this data, but outsiders are locked out.

So the three protection keywords are:

private

Access is limited to the class only.

protected

The class and any derived class that use the class as a base class can access the member.

public

Anyone can access the member.

Also, because m_stack is derived from stack , you can use an m_stack type variable wherever a stack type variable is used. In the following example, we create an m_stack named multi_stack that is used as a parameter to the function push_things , which takes a normal, unbounded stack as a parameter:

 void push_things(stack& a_stack) {     a_stack.push(1);     a_stack.push(2); } // ... m_stack multi_stack; // A random stack // .... push_things(bounded_stack); 

The function push_things takes a stack as a parameter. Even though the variable multi_stack is an m_stack type variable, C++ turns it into a stack when push_things is called.

One way to explain this is that although multi_stack is of type m_stack, when it is used by push_things, the function is looking through a peephole that allows it to see only the stack part of the variable, as shown in Figure 21-2.

Figure 21-2. How push_things sees an m_stack
figs/c++2_2102.gif

Let's improve the basic stack so that instead of always allocating a fixed-size stack, we allocate the stack dynamically. The new stack starts with:

 class stack {     private:         int *data;    // Pointer to the data in the stack     protected:         int count;    // Current item on the stack     public:         stack(const unsigned int size) {             data = new int[size];             count = 0;         };         virtual ~stack(  ) {             delete []data;             data = NULL;         } // ... 

(We discuss the keyword virtual later in this chapter.)

This stack is more flexible. To use the new stack, we must give it a size when we declare the stack variable. For example:

 stack big_stack(1000); stack small_stack(10); stack bad_stack; // Illegal, size required 

Back to the m_stack class: somehow we need to call the base class constructor ( stack ) with a parameter.

The way we do this is to put the base-constructor initialization just after the declaration of the constructor for the derived class.

But this flexibility creates some problems for the m_stack : the constructor for stack contains a parameter. How is the m_stack to initialize the simple stack ?

The solution is to use a syntax similar to initializing a constant data member:

 class m_stack: public stack {     private:         const unsigned int stack_size;   // Size of the simple stack     public:         m_stack(const unsigned int size) : stack(size),                                            stack_size(size) {         } 

So expression stack(size) calls the constructor for stack while stack_size(size) initializes the constant data member stack_size . (Or if you've got a warped mind, you can think of stack_size(size) as calling the constructor for the integer constant stack_size .)

Because the new version of stack uses dynamic memory ( new and delete ), it is vital that we define the "big four" member functions: the constructor, destructor, copy constructor, and assignment operator (=). When we use simple member variables to store our data, the default destructor would automatically reclaim all the memory we used. But now that we are using the heap, we must use delete to free the memory and that needs to be done in the destructor.

I l @ ve RuBoard


Practical C++ Programming
Practical C Programming, 3rd Edition
ISBN: 1565923065
EAN: 2147483647
Year: 2003
Pages: 364

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net