Skip to content. Skip to navigation

ICTP Portal

Sections
You are here: Home Manuals on-line PGI Compiler pgC_lib stdlibug 10.2 The stack Data Abstraction
Personal tools
Document Actions

10.2 The stack Data Abstraction



Click on the banner to return to the user guide home page.

10.2 The stack Data Abstraction

As a data abstraction, a stack is traditionally defined as any object that implements the following operations:

empty()
return true if the collection is empty
size()
return number of elements in collection
top()
return (but do not remove) the topmost element in the stack
push(newElement)
push a new element onto the stack
pop()
remove (but do not return) the topmost element from the stack

10.2.1 Include Files

Note that accessing the front element and removing the front element are separate operations. Programs that use the stack data abstraction should include the file stack, as well as the include file for the container type (e.g., vector).

   # include <stack>
   # include <vector>
Right Angle Brackets

10.2.2 Declaration and Initialization of stack

A declaration for a stack must specify two arguments; the underlying element type, and the container that will hold the elements. For a stack, the most common container is a vector or a deque, however a list can also be used. The vector version is generally smaller, while the deque version may be slightly faster. The following are sample declarations for a stack.

   stack< int, vector<int> > stackOne;
   stack< double, deque<double> > stackTwo;
   stack< Part *, list<Part * > > stackThree;
   stack< Customer, list<Customer> > stackFour;

The last example creates a stack of a programmer-defined type named Customer.

10.2.3 Example Program - A RPN Calculator

A classic application of a stack is in the implementation of calculator. Input to the calculator consists of a text string that represents an expression written in reverse polish notation (RPN). Operands, that is, integer constants, are pushed on a stack of values. As operators are encountered, the appropriate number of operands are popped off the stack, the operation is performed, and the result is pushed back on the stack.

Obtaining the Sample Program

We can divide the development of our stack simulation into two parts, a calculator engine and a calculator program. A calculator engine is concerned with the actual work involved in the simulation, but does not perform any input or output operations. The name is intended to suggest an analogy to a car engine, or a computer processor - the mechanism performs the actual work, but the user of the mechanism does not normally directly interact with it. Wrapped around this is the calculator program, which interacts with the user, and passes appropriate instructions to the calculator engine.

We can use the following class definition for our calculator engine. Inside the class declaration we define an enumerated list of values to represent each of the possible operators that the calculator is prepared to accept. We have made two simplifying assumptions: all operands will be integer values, and we will handle only binary operators.

class calculatorEngine {
public:
   enum binaryOperator {plus, minus, times, divide};
   
   int currentMemory ()           // return current top of stack
      { return data.top(); }
      
   void pushOperand (int value)   // push operand value on to stack
      { data.push (value); }
      
   void doOperator (binaryOperator);   // pop stack and perform
                                       // operator
   
protected:
   stack< int, vector<int> > data;
};
Defensive Programming

The member function doOperator() performs the actual work. It pops values from the stack, performs the operation, then pushes the result back onto the stack.

void calculatorEngine::doOperator (binaryOperator theOp)
{
   int right = data.top();   // read top element
   data.pop();   // pop it from stack
   int left = data.top();   // read next top element
   data.pop();   // pop it from stack
   switch (theOp) {
      case plus: data.push(left + right); break;
      case minus: data.push(left - right); break;
      case times: data.push(left * right); break;
      case divide: data.push(left / right); break;
      }
}

The main program reads values in reverse polish notation, invoking the calculator engine to do the actual work:

void main() {
   int intval;
   calculatorEngine calc;
   char c;
   
   while (cin >> c)
      switch (c) {
         case '0': case '1': case '2': case '3': case '4':
         case '5': case '6': case '7': case '8': case '9':
            cin.putback(c);
            cin >> intval;
            calc.pushOperand(intval);
            break;
         
         case '+':  calc.doOperator(calculatorEngine::plus);
            break;
   
         case '-':  calc.doOperator(calculatorEngine::minus);
            break;
   
         case '*':  calc.doOperator(calculatorEngine::times);
            break;
   
         case '/':  calc.doOperator(calculatorEngine::divide);
            break;

         case 'p':  cout << calc.currentMemory() << endl;
            break;
   
         case 'q':  return; // quit program
      }
}

©Copyright 1996, Rogue Wave Software, Inc.


Powered by Plone This site conforms to the following standards: