Essential Tools Module User's Guide : Chapter 6 Collection Classes : Iterators in Collection Classes
Iterators in Collection Classes
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 this 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 Essential Tools Module iterators—those declared as a distinct class related to the collection class—the rule is “advance and then return,” which is actually patterned after Stroustrup (1986, Section 7.3.2). However, iterators obtained directly from classes implemented using the C++ Standard 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. The ANSI standard describes container iterators in great detail. A Standard Library iterator must always be compared against that collection's end() iterator to discover if it truly references an item in the container, or if it is “one-past-the-last-element.”
Traditional Essential Tools Module Iterators
Traditional Essential Tools Module 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 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 has just been constructed. It's OK to change a collection via the iterator itself.
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 the Essential Tools Module, 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 must be traversed, 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.
The Essential Tools Module provides const versions of every traditional iterator. Const iterators have the same capabilities as traditional iterators, but also maintain const correctness by not allowing the modification of a container via that iterator. If you attempt to modify a container with a const iterator, a compilation error will occur.