Ordered-Access Collections

Queue ADT

Implementation concerns

Implementation as queue template class

 

template < class T >

class Queue

{

    public:

                Queue ( );  // constructs an empty queue

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

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

                ~Queue ( );  // deallocates associated memory

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

                void Push ( const T& );

                T Pop ( void );

                // No "Top" operation; cannot look at top without removing

                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 queue, then undo into original queue

    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

                int first_index;         // Location of top element of queue

};

 

template < class T >

Queue < T > :: Queue ( ) : 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

    first_index = 0;   // No elements currently stored, but num_elements is 0 also

}

template < class T >

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

{

    num_elements = rhs . num_elements;

    capacity = rhs . capacity;

    first_index = 0 ;  // Keep elements to the left of the new array

    array = new T [ capacity ];

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

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

}

template < class T >

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

{

    if ( this != & rhs )

        {

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

            num_elements = rhs . num_elements;

            capacity = rhs . capacity;

            first_index = 0;  // Keep elements to left of new array

            array = new T [ capacity ];

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

                array [ i ] = rhs . array [ rhs . first_index + i ] ;  // Won't iterate if num_elements is 0

            }

    return *this;

}

template < class T >

T Queue < T > :: Pop ( void )  // Remove and return first element

{

    if ( num_elements == 0 )

        // need to throw some exception

    T return_value = array [ first_index ];

    num_elements -- ;

    first_index ++;

    // Now we may need to contract array

    if ( num_elements < capacity / 4  )

        {

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

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

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

        delete [ ] array;

        array = new_array;

        capacity /= 2;

        first_index = 0;

        }

    return return_value;

}

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

{

    // If we need to use room at the beginning, slide everything over.

    if ( first_index + num_elements == capacity && num_elements < capacity )

        {

         first_index = 0;

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

            array [ i ] = array [ i + capacity - num_elements ];

        }

    // Otherwise, if all spaces are occupied, allocate more space.

    else if ( first_index + num_elements == capacity )

        {

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

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

            new_array [ i ] = array [ first_index + i ];

        delete [  ] array;

        array = new_array;

        capacity *= 2;

        first_index = 0;

        }

    array [ first_index + num_elements ] = value;

    num_elements ++;

}

template < class T >

void Queue < T > :: Clear ( void ) // Empty out the queue

{

    delete [ ] array;

    num_elements = first_index = 0;

    capacity = 1;

    array = new T [ 1 ];

}

template < class T >

Queue < T> :: ~Queue ( )

{

    delete [ ] array;

}

template < class T >

int Queue < T > :: NumElements ( void )

{

    return num_elements;

}

template < class T >

bool Queue < T > :: IsEmpty ( void )

{

    return ( num_elements == 0 )

}

template < class T >

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

{

    return capacity;

}

template < class T >

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

{

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

    for ( int i = first_index ; i < first_index + num_elements; i ++ )  // First element first

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

    o << endl;

}

 

Queue Usage Examples

Queue < Polynomial > PolyQueue ;

Queue < double > DoubleQueue;

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

   DoubleQueue . Push ( i ) ;          // Pushes doubles 0 - 49 onto Queue

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

   PolyQueue . Push ( Polynomial ( i, SomeArray[ i ]  );  // Pushes new Polynomials onto Queue

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

    DoubleQueue . Pop ( );  // DoubleQueue now holds doubles 5 - 49

PolyQueue . Print ( ) ;  // Will use Polynomial's << operator