Ordered-Access Collections

Stack ADT

Implementation concerns

Implementation as stack template class

 

template < class T >

class Stack

{

    public:

                Stack ( );  // constructs an empty stack

                Stack ( const Stack < T > & ) ; // copy constructor

                Stack& operator= ( const stack < T > & );  // assignment               

                ~Stack ( );  // deallocates associated memory

                void Clear ( );  // removes all existing elements from stack

                void Push ( const T& );

                T Pop ( void );

                T& Top ( void );   // Allows us to change Top; could have popped and re-pushed

                int NumElements ( void );

                bool IsEmpty ( void );

                int Capacity ( void );   // User may want to know how long before reallocation

                void Print ( ostream& o = cout );  // Doesn't break access rules

                             //  We could Pop, print, Push onto another stack, then undo onto original stack

    private:

                int num_elements;   // The number of T objects currently being stored

                T* array;

                int capacity;            // The number of T objects we currently have room for

};

 

template < class T >

Stack < T > :: Stack ( ) : num_elements ( 0 ) , capacity ( 1 )// constructor

{

    array = new T [ 1 ] ;   // Since 2 * 0 is still 0, we want to start with capacity 1

                                      //  new [ ] syntax allows us to always use delete [ ] later

}

template < class T >

Stack < T > :: Stack ( const Stack < T > & rhs ) // copy constructor

{

    num_elements = rhs . num_elements;

    capacity = rhs . capacity;

    array = new T [ capacity ];

    for ( int i = 0; i < num_elements ; i++ )

        array [ i ] = rhs . array [ i ];

}

template < class T >

Stack < T > &  Stack < T > :: operator = ( const Stack < T > & ) // assignment operator

{

    if ( this != & rhs )

        {

            delete [ ] array;   // Empty out the left-hand side

            num_elements = rhs . num_elements;

            capacity = rhs . capacity;

            array = new T [ capacity ];

            for ( int i = 0; i < capacity ; i ++ )

                array [ i ] = rhs . array [ i ] ;

            }

    return *this;

}

template < class T >

T& Stack < T > :: Top ( void ) // Peek at top element

{

    if ( num_elements )

        return array [ num_elements - 1 ];

    // otherwise need to throw an exception

}

template < class T >

T Stack < T > :: Pop ( void )  // Remove and return top element

{

    if ( num_elements == 0 )

        // need to throw some exception

    T return_value = array [ num_elements - 1 ];

    num_elements -- ;

    // Now we may need to contract array

    if ( num_elements < capacity / 4  )

        {

        T* new_array = new T [ max ( capacity / 2, 1 ) ] ;  // Don't go below 0!

        for ( int i = 0; i < num_elements; i ++ )

            new_array [ i ] = array [ i ];  // Only copy over as many elements as are being stored

        delete [ ] array;

        array = new_array;

        capacity = max ( capacity / 2, 1 );

        }

    return return_value;

}

template < class T >
void Stack < T > :: Push ( const T& value )

{

    // If we are already at capacity, we need to expand the array first.

    if ( num_elements == capacity )

        {

         T* new_array = new T [ capacity * 2 ];

         for ( int i = 0; i < num_elements; i ++ )

            new_array [ i ] = array [ i ];

        delete [  ] array;

        array = new_array;

        capacity *= 2;

        }

    array [ num_elements ] = value;

    num_elements ++;

}

template < class T >

void Stack < T > :: Clear ( void ) // Empty out the stack

{

    delete [ ] array;

    num_elements = 0;

    capacity = 1;

    array = new T [ 1 ];

}

template < class T >

Stack < T> :: ~Stack ( )

{

    delete [ ] array;

}

template < class T >

int Stack < T > :: NumElements ( void )

{

    return num_elements;

}

template < class T >

bool Stack < T > :: IsEmpty ( void )

{

    return ( num_elements == 0 )

}

template < class T >

int Stack < T > :: Capacity ( void )  // Returns total amount of space allocated

{

    return capacity;

}

template < class T >

void Stack < T > :: Print ( ostream& o )

{

    o << "Stack has " << num_elements << " elements." << endl;

    for ( int i = num_elements - 1 ; i >= 0; i -- )  // Print top element first

        o <<  array [ i ] << " ";  // Assumes T class implements << operator

    o << endl;

}

 

Stack Usage Examples

Stack < VarInt > VarIntStack ;

Stack < double > DoubleStack;

for ( int i = 0; i < 50; i ++ )

   DoubleStack . Push ( i ) ;          // Pushes doubles 0 - 49 onto stack

for ( int i = 0; i < 10; i  ++ )

   VarIntStack . Push ( itoa ( i ) );  // Pushes hex numbers 0 - 9 onto stack

DoubleStack . Top ( ) = 2.5;  // DoubleStack is now 0.0 - 48.0, 2.5

for ( int i = 0 ; i < 5; i ++ )

    VarIntStack . Pop ( );  // VarIntStack now holds hex numbers 0 - 4

VarIntStack . Print ( ) ;  // Will use VarInt's << operator