An iterator for a container class C, provides the ability to walk through the elements of C. We've already seen iterators - we just didn't use that name. The cursor we used for our list class provide the ability to walk through the items on the list. That is an example of an internal iterator.
Most importantly, you cannot have use a cursor to traverse the list in more than one way simultaneously.
For example, suppose we wanted to check whether something a list represents a palindrome. We'd like to be able to check without using lists or stacks. We'd like to be able to iterate from both ends, in an interleaved fashion: is the first item on the list equal to the last? is the second item equal to the second to last item? etc.
To get around these problems we can define an external iterator - a separate class that has access to the private members of the container class (the external iterator is a friend). For linked-lists we might have:
template <class Item> class ListIter { public: // CONSTRUCTOR ListIter(const List<Item>& l); // MODIFICATION MEMBER FUNCTIONS void reset(); void atEnd(); void atLast(); void advance(); void back(); void find(const Item& item); // CONSTANT MEMBER FUNCTIONS bool isFront() const; bool isEnd() const; bool isLast() const; Item current() const; private: // MEMBER VARIABLES const List<Item> *listPtr; // pointer to the list ListItem<Item> *location; // the cursor };
Notice that listPtr is declared to be const. This is because once we have created an iterator for a particular list it should remain an iterator for that list only.
For arrays we might have the same public interface, and these private members:
const List<Item> *listPtr; // pointer to the list size_t location; // the cursor
Implementation is almost identical to the original implementation. The only change is that members of the list must be resolved using listPtr. For example, for linked lists:
template <class Item> bool ListIter<Item>::isFront() const { return (location==listPtr->head); }
The constructor for the iterator takes a reference to a list as an argument. It associates the iterator to that list and sets the cursor to the front of the list. For example, for arrays:
template <class Item> ListIter<Item>::ListIter(const List<Item>& l) : listPtr(&l), location(0) { }
Of course, the interface for the List class template changes slightly. Internal cursor operations (reset, advance, etc.) are now found in the external iterator class template. Functions that relied on the internal iterator now take explicit iterator arguments:
void addBefore(const ListIter(Remove's cursor argument is not const because removal can effect the position of the cursor.)- & cursor, const Item& entry); void addAfter(const ListIter
- & cursor, const Item& entry); void remove(ListIter
- & cursor);
The use of external iterators remain very similar to the use of internal ones. To walk through a list of type Foo, assuming we have declared an external iterator as:
ListIter<Foo> iter;we can say:
for(iter.reset(); !iter.isEnd(); iter.advance()) // perform some action
A version of our List class template using linked-lists with an external iterator ListIter class template can be found here. Also available in that directory is another implementation of the palindromes program using two external iterators.