Many of the collection classes have an associated iterator. The advantage of the iterator is that it maintains its own internal state, thus allowing two important benefits:
More than one iterator can be constructed from the same collection class;
All of the items need not be visited in a single sweep.
Iterators are always constructed from the collection class itself, as in the following example:
RWBinaryTree bt; . . . RWBinaryTreeIterator bti(bt);
Immediately after construction, or after reset()is called, the state of the iterator is undefined. You must either advance it or position it before using its current state or position.
For traditional Tools.h++ iterators -- those declared as a distinct class related to the collection class -- the rule is "advance and then return."[10] However, iterators obtained directly from classes implemented using the Standard C++ Library differ. In keeping with the standard for container classes, they follow the precept: If you obtain an iterator using the begin() or end() method, or using an algorithm which returns an iterator, you have a "Standard Library" iterator.[11] A Standard Library iterator must always be compared against that collection's end() iterator to discover if it references an item in the container.
Traditional Tools.h++ iterators have a number of unique features.
You recall that the state of the iterator is undefined immediately following construction or the calling of reset(). You also trigger the undefined state if you change the collection class directly[12] by adding or deleting objects while an iterator is active. Using an iterator at that point can bring unpredictable results. You must then use the member function reset() to restart the iterator, as if it had just been constructed.
At any given moment, the iterator marks an object in the collection class -- think of it as the current object. There are various methods for moving this mark. For example, most of the time you will probably be using member function operator(). In Tools.h++, it is designed to always advance to the next object, then return either TRUE or a pointer to the next object, depending on whether the associated collection class is value-based or reference-based, respectively. It always returns FALSE (i.e., zero) when the end of the collection class is reached. Hence, a simple canonical form for using an iterator is:
RWSlistCollectable list; . . . RWSlistCollectableIterator iterator(list); RWCollectable* next; while (next = iterator()) { . . // (use next) . }
As an alternative, you can also use the prefix increment operator ++X. Some iterators have other member functions for manipulating the mark, such as findNext() or removeNext().
Member function key() always returns either the current object or a pointer to the current object, again depending on whether the collection class is value-based or reference-based, respectively.
For most collection classes, using member function apply() to access every member is much faster than using an iterator. This is particularly true for the sorted collection classes -- usually a tree has to be traversed here, requiring that the parent of a node be stored on a stack. Function apply() uses the program's stack, while the sorted collection class iterator must maintain its own. The former is much faster.