cpp
// Using basic iterators
#include <iostream>
#include <vector>
#include <iterator>
using namespace std;
template<class Cont, class PtrMemFun>
void apply(Cont& c, PtrMemFun f) {
typename Cont::iterator it = c.begin();
while(it != c.end()) {
(it->*f)(); // Compact form
((*it).*f)(); // Alternate form
it++;
}
}
class Z {
int i;
public:
Z(int ii) : i(ii) {}
void g() { i++; }
friend ostream&
operator<<(ostream& os, const Z& z) {
return os << z.i;
}
};
Chapter 15: Multiple Inheritance
642
int main() {
ostream_iterator<Z> out(cout, " ");
vector<Z> vz;
for(int i = 0; i < 10; i++)
vz.push_back(Z(i));
copy(vz.begin(), vz.end(), out);
cout << endl;
apply(vz, &Z::g);
copy(vz.begin(), vz.end(), out);
} ///:~
Because operator-> is defined for STL iterators, it can be used for pointer-to-member dereferencing (in the following chapter you’ll learn a more elegant way to handle the problem of applying a member function or ordinary function to every object in a container).
Much of the time, this is all you need to know about iterators – that they are produced by
begin( ) and end( ), and that you can use them to move through a container and select elements. Many of the problems that you solve, and the STL algorithms (covered in the next
chapter) will allow you to just flail away with the basics of iterators. However, things can at times become more subtle, and in those cases you need to know more about iterators. The rest
of this section gives you the details.
Iterators in reversible containers
All containers must produce the basic iterator. A container may also be reversible, which means that it can produce iterators that move backwards from the end, as well as the iterators that move forward from the beginning.
A reversible container has the methods rbegin( ) (to produce a reverse_iterator selecting the end) and rend( ) (to produce a reverse_iterator indicating “one past the beginning”). If the container is const then rbegin( ) and rend( ) will produce const_reverse_iterators.
All the basic sequence containers vector, deque and list are reversible containers. The following example uses vector, but will work with deque and list as well:
//: C20:Reversible.cpp
// Using reversible containers
#include "../require.h"
#include <vector>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main() {
ifstream in("Reversible.cpp");
Chapter 15: Multiple Inheritance
643
assure(in, "Reversible.cpp");
string line;
vector<string> lines;
while(getline(in, line))
lines.push_back(line);
vector<string>::reverse_iterator r;
for(r = lines.rbegin(); r != lines.rend(); r++)
cout << *r << endl;
} ///:~
You move backward through the container using the same syntax as moving forward through
a container with an ordinary iterator.
The associative containers set, multiset, map and multimap are also reversible. Using iterators with associative containers is a bit different, however, and will be delayed until those containers are more fully introduced.
Iterator categories
The iterators are classified into different “categories” which describe what they are capable of doing. The order in which they are generally described moves from the categories with the
most restricted behavior to those with the most powerful behavior.
Input: read-only, one pass
The only predefined implementations of input iterators are istream_iterator and
istreambuf_iterator, to read from an istream. As you can imagine, an input iterator can only be dereferenced once for each element that’s selected, just as you can only read a particular portion of an input stream once. They can only move forward. There is a special constructor
to define the past-the-end value. In summary, you can dereference it for reading (once only
for each value), and move it forward.
Output: write-only, one pass
This is the complement of an input iterator, but for writing rather than reading. The only
predefined implementations of output iterators are ostream_iterator and
|