Iterators are pure abstractions: Anything that behaves like an
iterator is an iterator. For this reason, you can write classes that have
the interface of iterators but do something (completely) different. The C++
standard library provides several predefined special iterators: iterator
adapters. They are more than auxiliary classes; they give the whole concept
a lot more power.
The following subsections introduce three iterator adapters:
-
Insert iterators
-
Stream iterators
- Reverse iterators
Insert Iterators
The first example of iterator adapters are insert iterators, or
inserters. Inserters are used to let algorithms operate in insert mode
rather than in overwrite mode. In particular, they solve the problem of
algorithms that write to a destination that does not have enough room: They let
the destination grow accordingly.
Insert iterators redefine their interface internally as follows:
-
If you assign a value to their actual element, they insert that value into the collection to which they belong. Three different insert iterators have different abilities with regard to where the elements are inserted — at the front, at the end, or at a given position.
-
A call to step forward is a no-op.
Consider the following example:
This example uses all three predefined insert iterators:
-
Back insertersBack inserters insert the elements at the back of their container (appends them) by calling push_back(). For example, with the following statement, all elements of coll1 are appended into coll2:
Of course, back inserters can be used only for containers that provide push_back() as a member function. In the C++ standard library, these containers are vector, deque, and list. -
Front insertersFront inserters insert the elements at the front of their container by calling push_front(). For example, with the following statement, all elements of coll1 are inserted into coll3:
Note that this kind of insertion reverses the order of the inserted elements. If you insert 1 at the front and then 2 at the front, the 1 is after the 2.Front inserters can be used only for containers that provide push_front() as a member function. In the C++ standard library, these containers are deque and list. -
General insertersA general inserter, also called simply an inserter, inserts elements directly in front of the position that is passed as the second argument of its initialization. It calls the insert() member function with the new value and the new position as arguments. Note that all predefined containers have such an insert() member function. This is the only predefined inserter for associative containers.But wait a moment. I said that you can't specify the position of a new element in an associative container because the positions of the elements depend on their values. The solution is simple: For associative containers, the position is taken as a hint to start the search for the correct position. If the position is not correct, Table below describes a user-defined inserter that is more useful for associative containers.
Table : Predefined Insert Operators
Expression
|
Kind of Inserter
|
back_inserter (container)
|
Appends
in the same order by using push_back()
|
front_inserter (container)
|
Inserts
at the front in reverse order by using push_front()
|
inserter (container ,pos)
|
Inserts
at pos (in the same order) by using insert()
|
Stream Iterators
Another very helpful kind of iterator adapter is a stream iterator. Stream iterators are iterators that read from and write to a stream (A stream is an object that represents I/O channels). Thus, they provide an abstraction that lets the input from the keyboard behave as a collection, from which you can read. Similarly you can redirect the output of an algorithm directly into a file or onto the screen.
Consider the following example. It is a typical example of the power of the whole STL. Compared with ordinary C or C++, it does a lot of complex processing by using only a few statements:
The program has only three statements that read all words from the standard
input and print a sorted list of them. Let's consider the three statements
step-by-step. In the statement
two input stream iterators are used:
-
The expression
creates a stream iterator that reads from the standard input stream cin. The template argument string specifies that the stream iterator reads elements of this type. These elements are read with the usual input operator >>. Thus, each time the algorithm wants to process the next element, the istream iterator transforms that desire into a call of
The input operator for strings usually reads one word separated by whitespaces, so the algorithm reads word-by-word. -
The expression
calls the default constructor of istream iterators that creates an end-of-stream iterator. It represents a stream from which you can no longer read.
As usual, the copy() algorithm operates as long as
the (incremented) first argument differs from the second argument. The
end-of-stream iterator is used as the end of the range, so the algorithm
reads all strings from cin until it can no longer read
any more (due to end-of-stream or an error). To summarize, the source of the
algorithm is "all words read from cin." These words are
copied by inserting them into coll with the help of a
back inserter.
The sort() algorithm sorts all elements:
Lastly, the statement
copies all elements from the collection into the destination cout. During the process, the unique_copy() algorithm eliminates adjacent duplicate
values. The expression
creates an output stream iterator that writes strings
to cout by calling operator >> for each element.
The second argument behind cout serves as a separator
between the elements. It is optional. In this example, it is a newline, so every
element is written on a separate line.
All components of the program are templates, so you can change the program
easily to sort other value types, such as integers or more complex objects.
In this example, one declaration and three statements were used to sort all
words from standard input. However, you could do the same by using only one
declaration and one statement.
Reverse Iterators
The third kind of predefined iterator adapters are reverse iterators. Reverse
iterators operate in reverse. They switch the call of an increment operator
internally into a call of the decrement operator, and vice versa. All containers
can create reverse iterators via their member functions rbegin() and rend(). Consider the
following example:
The expression
returns a reverse iterator for coll. This iterator
may be used as the beginning of a reverse iteration over the elements of the
collection. Its position is the last element of the collection. Thus, the
expression
returns the value of the last element.
Accordingly, the expression
returns a reverse iterator for coll that may be used
as the end of a reverse iteration. As usual for ranges, its position is past the
end of the range, but from the opposite direction; that is, it is the position
before the first element in the collection.
The expression
is as undefined as is
You should never use operator * (or operator ->) for a position that does not represent a valid
element.
The advantage of using reverse iterators is that all algorithms are able to
operate in the opposite direction without special code. A step to the next
element with operator ++ is redefined into a step
backward with operator --. For example, in this case,
copy() iterates over the elements of coll from the last to the first element. So, the output of
the program is as follows:
You can also switch "normal" iterators into reverse iterators, and vice
versa. However, in doing so the element of an iterator changes.
-----------------------------------------------------------------
See Also:
-----------------------------------------------------------------
See Also:
-----------------------------------------------------------------
- Complete Tutorial of C++ Template's
- Standard Template Library Tutorial
- Inter Process Communication Tutorial
- Advance Programming in C & C++
-----------------------------------------------------------------
No comments:
Post a Comment