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:
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 stackNow 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:
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_stackLet'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 .)
|
I l @ ve RuBoard |