It's often convenient to package data together so we can move it around a program en masse, and access it in useful ways. Computer scientists have designed many different data structures to fill these needs. Collectively, these structures are called container classes in C++ and collections in Java. A basket is a familiar analogy for a data structure: At some point, there may be too many items for us to hold in our hands, but if we put them in a basket, we can hold many items by holding the basket's handle. Most data structures also organize the stored data and allow programmers to access it in specific ways. Linear data structures (e.g., an ArrayList, a vector, or a string) organize their contents serially in a line, which makes sequential or linear access relatively easy.
Programmers use iterators to access the data or elements stored in containers and collections. Each iterator object is tightly bound to a data object (an instance of a container or collection) and returns the data object's elements one at a time. In the case of string objects, iterators return the elements or characters in sequential order, beginning with either the first or last character. As an introductory example to iterators, the ipalnumber example solves the palindrome-number problem using string iterators.
The string class has eight member functions that create different kinds of iterators. The iterators work in pairs to provide a unique combination of three distinct features:
The iterators appropriately override or reuse some C++ operators for getting the characters from a string in sequential order. The following table summarizes a few member functions and operators needed to solve the palindrome-number problem with iterators.
string Member Functions | |
---|---|
begin |
Returns an iterator that points to the first character in the string. The iterator naturally moves or iterates from the first character of the string to the last, that is, from left to right. for (string::iterator i = s.begin(); i != s.end; i++) |
end |
Returns an iterator that points to the last character in the string (it actually points to one character beyond the last character - to what would be the null-termination character in a C-string - which makes looping through the string in the forward direction easy but complicates looping in the reverse direction). |
rbegin |
Returns an iterator that points to the last character in the string - the beginning of the string going in the reverse direction. The natural looping or iteration order is from the end of the string to the beginning or right to left.
for (string::reverse_iterator r = s.rbegin(); r != s.rend(); r++) |
rend |
Returns an iterator that points to the first character in the string (it actually points to the location one character before the beginning of the string), which makes looping through the string in the reverse direction easy but complicates looping in the forward direction. |
Iterator Operators | |
* |
The dereference operator - returns the character at the current iteration location. |
++ |
The auto-increment operator (both pre and post versions). Advances the iterator to the next character, where the meaning of "next" depends on the kind of iterator. |
-- |
The auto-decrement operator (both pre and post versions). Reverses the iterator to the previous character, where the meaning of "previous" depends on the kind of iterator. |
The main and to_string functions remain unchanged from the previous string solution. The following iterator solutions only affect the reverse function.
string reverse(string s) { string r; // (a) string::reverse_iterator i = s.rbegin(); // (b) while(i != s.rend()) // (c) r += *i++; // (d) return r; // (e) } |
|
// alternate version: replaces (b), (c), and (d) for (string::reverse_iterator i = s.rbegin(); i != s.rend(); i++) // (f) r += *i; // (g) |
++
has a higher precedence than does the indirection operator, *
. But also recall that the post version of auto-increment (where the "++" follows the expression) means to "Use the current value and then increment" (see Figure 1). So, the result of the expression *i++
is to dereference the iterator to get the character to which the iterator currently points and then increment or advance the iterator (which, since it is a reverse iterator, moves it to the left). Finally, the +=
operator concatenates the character to the reversed string ri != s.rend()
is true while the iterator, i, is accessing the last character but becomes false after the iterator's last increment*
, returns the current string character referenced by the iterator, and the concatenation-with-assignment operator, +=
, appends the character to the right side of the string r
string reverse(string s) { string r; // (a) string::iterator i = s.end(); // (b) while(i != s.begin()) // (c) r += *--i; // (d) return r; // (e) } |
*--i
backs up one position and then gets the character at the new position by dereferencing the iterator with the dereference operator, *
. Then the +=
operator concatenates the character to the reversed string r