// QueueDemo2.cpp // CSC 601 - Fall 2002 - Kirby // ------------------------------------------------------------------------ // // Enhancing our abstract IQueue with an iterator, and consequent use of // factory method and auto_ptrs. // // Written in class, Thursday 8/29. // // This is "pedagogical" code, meant to make certain design and C++ points, // and should not be viewed as a polished, optimal product. // (Of course, all we really needed was deque.) // // For convenience, we dumped everything in one source file. // ------------------------------------------------------------------------ // IQueue.h // -------- #include #include #include typedef int T ; // just for now class EmptyQueueException : public std::runtime_error { public: EmptyQueueException( const std::string& msg ) : std::runtime_error( msg ) {} } ; struct IRearwardConstIterator { // inspectors virtual const T& current() const throw ( std::domain_error ) =0 ; virtual bool atEnd() const throw( std::domain_error ) =0 ; // mutators virtual void reset() throw( std::domain_error ) =0 ; virtual void advance() throw( std::domain_error ) =0 ; // virtual dtor virtual ~IRearwardConstIterator() {} ; } ; struct IQueue { // inspectors virtual IRearwardConstIterator* new_iterator() const throw () =0 ; virtual int length() const throw() =0 ; virtual const T& front() const throw ( EmptyQueueException ) =0 ; virtual const T& rear() const throw ( EmptyQueueException ) =0 ; bool empty() const throw() { return length()==0 ; } // mutators virtual T& rear() throw ( EmptyQueueException ) =0 ; virtual void push( const T& item ) throw ( std::exception ) =0 ; virtual void pull() throw ( EmptyQueueException ) =0 ; // virtual dtor virtual ~IQueue() {} ; } ; // ArrayQueue.h // ------------ // A concrete class implementing of IQueue with a fixed circular array. // #include "IQueue.h" class ArrayQueue ; // forward decl class ArrayQueueIterator : public IRearwardConstIterator { public: ArrayQueueIterator( const ArrayQueue* pTarget ) throw () ; ~ArrayQueueIterator() throw() {} // inspectors const T& current() const throw ( std::domain_error ) ; bool atEnd() const throw( std::domain_error ) ; // mutators void reset() throw ( std::domain_error ) ; void advance() throw ( std::domain_error ) ; private: const ArrayQueue * const _pTarget ; // Note carefully! int _iCurrent ; } ; class ArrayQueue : public IQueue { public: ArrayQueue( int maxLength ) throw ( std::bad_alloc ) ; ~ArrayQueue() throw() ; // inspectors ArrayQueueIterator* new_iterator() const throw () ; // still an override, despite return type change int length() const throw() ; const T& front() const throw ( EmptyQueueException ) ; const T& rear() const throw ( EmptyQueueException ) ; // mutators T& rear() throw ( EmptyQueueException ) ; void push( const T& item ) throw ( std::out_of_range ) ; void pull() throw ( EmptyQueueException ) ; // constants const int MAXLENGTH ; private: T* _ar ; // _ar[ _front..._rear ] (circularly) holds queue contents int _front, _rear, _length ; friend class ArrayQueueIterator ; } ; // ArrayQueue implementation // inline ArrayQueue::ArrayQueue( int maxLength ) throw ( std::bad_alloc ) : MAXLENGTH( maxLength ) , _ar( new T[ maxLength ] ) , _front( 0 ) , _rear( maxLength-1 ) , _length( 0 ) { } inline ArrayQueue::~ArrayQueue() throw() { delete [] _ar ; } inline ArrayQueueIterator* ArrayQueue::new_iterator() const throw () { return new ArrayQueueIterator( this ) ; } inline int ArrayQueue::length() const throw() { return _length ; } inline const T& ArrayQueue::front() const throw ( EmptyQueueException ) { if ( empty() ) throw EmptyQueueException( "attempt to inspect front of empty ArrayQueue." ) ; return _ar[ _front ] ; } inline const T& ArrayQueue::rear() const throw ( EmptyQueueException ) { if ( empty() ) throw EmptyQueueException( "attempt to inspect rear of empty ArrayQueue" ) ; return _ar[ _rear ] ; } inline T& ArrayQueue::rear() throw ( EmptyQueueException ) { if ( empty() ) throw EmptyQueueException( "attempt to access rear of empty ArrayQueue" ) ; return _ar[ _rear ] ; } inline void ArrayQueue::push( const T& item ) throw ( std::out_of_range ) { if ( _length == MAXLENGTH ) throw std::out_of_range( "Attempting to push on a full ArrayQueue." ) ; _rear= (_rear + 1 ) % MAXLENGTH ; _ar[ _rear ]= item ; _length++ ; } inline void ArrayQueue::pull() throw ( EmptyQueueException ) { if ( empty() ) throw EmptyQueueException( "attempt to pull from empty ArrayQueue" ) ; _front= ( _front + 1 ) % MAXLENGTH ; _length-- ; } // ArrayQueueIterator implementation // // OMITTED: Code to handle stale iterators (we will discuss this in class 9/3). // Idea: methods throw domain_errors if a mutator has been called on the // target of the iterator. Pattern suggestion: Observer. // inline ArrayQueueIterator::ArrayQueueIterator( const ArrayQueue* pTarget ) throw () : _pTarget( pTarget ) , _iCurrent( pTarget->_front ) { } inline const T& ArrayQueueIterator::current() const throw ( std::domain_error ) { if ( atEnd() ) throw ( std::domain_error( "attempt to access beyond end of queue" ) ) ; return _pTarget->_ar[ _iCurrent ] ; } inline bool ArrayQueueIterator::atEnd() const throw ( std::domain_error ) // ** A tiny bug: what happens with length==MAXLENGTH? You are invited to fix this! { return _iCurrent == ( _pTarget->_rear + 1 ) %_pTarget->MAXLENGTH ; } inline void ArrayQueueIterator::reset() throw ( std::domain_error ) { _iCurrent= _pTarget->_front ; } inline void ArrayQueueIterator::advance() throw ( std::domain_error ) { _iCurrent= ( _iCurrent + 1 ) % _pTarget->MAXLENGTH ; } // QueueDemo.cpp // ------------- // Note how auto_ptrs are essential here. // Warning: pit->reset() and pit.reset() are BOTH legal here. (Explain!) // #include "ArrayQueue.h" #include #include using namespace std ; void print( const IQueue& q ) { auto_ptr< IRearwardConstIterator > pit( q.new_iterator() ) ; for ( ; ! pit->atEnd() ; pit->advance() ) cout << pit->current() << endl ; } int main() { // Construct a queue, but refer to it abstractly (to test interface). ArrayQueue aq( 10 ) ; IQueue& q= aq ; // Push some stuff. for ( int i=0 ; i < 5 ; ++i ) q.push( i*100 ) ; // Demo our iterator. print( q ) ; cout << "Done." << endl ; return 0 ; }