(or in the open( ) member function), and writing happens in the single line (which defaults to sending the output to cout):
file.write();
The bulk of the program is involved with actually modifying the file in memory.
A plethora of iterators
As mentioned earlier in this and the previous chapter, an iterator is an abstraction that allows code to be generic, that is, to work with different types of containers without knowing the underlying structure of those containers. Most containers support iterators.[92] You can always say:.
to produce the types of the iterators produced by that container. Every container has a begin( ) member function that produces an iterator indicating the beginning of the elements in the container, and an end( ) member function that produces an iterator which is the as the
All iterators can advance within their sequence (via operator++) and allow == and != comparisons. Thus, to move an iterator it forward without running it off the end, you say something like:.
while(it != pastEnd) {
// Do something
it++;
}
in which pastEnd is the past-the-end marker produced by the container’s end( ) member function.
An iterator can be used to produce the element that it is currently selecting within a container through the dereferencing operator (operator*). This can take two forms. If it is an iterator and f( ) is a member function of the objects held in the container that the iterator is pointing within, you can say either:.
(*it).f();
or.
it->f();
Knowing this, you can create a template that works with any container. Here, the apply( ) function template calls a member function for every object in the container, using a pointer to member that is passed as an argument:.
//: C07:Apply.cpp
// Using simple iteration
#include
#include
#include
using namespace std;
template
void apply(Cont& c, PtrMemFun f) {
typename Cont::iterator it = c.begin();
while(it != c.end()) {
((*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;
}
};
int main() {
ostream_iterator
vector
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);
} ///:~
You can’t use operator-> in this case, because the resulting statement would be
(it->*f)();
which attempts to use the iterator’s operator->*, which is not provided by the iterator classes.[93]
It is much easier to use either for_each( ) or transform( ) to apply functions to sequences anyway, as you saw in the previous chapter.
Iterators in reversible containers
A container may also be
A reversible container has the member functions 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, rbegin( ) and rend( ) will produce const_reverse_iterators.
The following example uses vector, but will work with all containers that support iteration:
//: C07:Reversible.cpp
// Using reversible containers
#include
#include
#include
#include
#include "../require.h"
using namespace std;
int main() {
ifstream in("Reversible.cpp");
assure(in, "Reversible.cpp");
string line;
vector
while(getline(in, line))
lines.push_back(line);
for(vector
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.
Iterator categories
The iterators are classified into "categories" that describe their capabilities. 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