Notes on Iterators

(related reading: Main & Savitch, pp. 296-301; Wang pp. 276-278)

Deja Vu

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.

Problems with internal iterators

External Iterators

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& cursor, const Item& entry);
void addAfter(const ListIter& cursor, const Item& entry);
void remove(ListIter& cursor);
(Remove's cursor argument is not const because removal can effect the position of the 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.