float * ptr;
p++;
p = p + sizeof(float);
This means that incrementing the pointer is only meaningful as an iterator over arrays.
class iterator {
public:
iterator (node *p=0) {ptr=p};
iterator operator++(int);
char * operator*() {return(ptr->value);};
node * get_ptr() { return(ptr);};
bool not_null() { return(ptr != 0);};
private:
node *ptr;
} iterator
iterator::operator++(int)
{
if (ptr != 0)
ptr=ptr->next;
return *this;
}
Why not just overload the ++ and * operators on the list class rather than create a whole class? Functionally, there isn't much difference. Putting the operators in the list class, kind of merges two kinds of objects. The list class encapsulates all the information we have about the list in one place. Including the iterator operations in there mushes the two concepts. Creating an iterator class encapsulates what we know about the pointers in its own class. This is a little vague. The usefulness of a separate iterator class should become clearer when we talk about iterator adaptor classes. The key feature of an iterator over a simple pointer is the ability to assign arbitrary code to the operators * and ++. Again, this will be clearer in iterator adaptors. Iterators are used heavily in the algorithms in the STL. This is because they can be defined so that all iterators implement the same methods even though they are iterating over many different types.
Iterators are like pointers, you dereference the pointer
using the * operator.
You can add to the end of a vector using the push_back() method
but there is no push_front() method.
Some examples are:
vector
vector::iterator i=v.begin(); // i points to the first element in v
i++; // move to the next element
i--; // move to the previous element
v[3]=9; // set element 3 to 9
p++;
x=*p;
cin++
to go to the next input element and
*cin
to fetch it.
When we have seen problems like this before, we have overloaded
the operators to make them act they way we want to.
We will do that here by building an adaptor class to make the
iostream look like an iterator class.
The STL contains an adaptor class called istream_iterator
that overloads the increment and dereference operators so that
cin++
doesn't do anything.
The code probably looks like
istream_iterator & operator++() {};
The * operator simply reads the next thing from the input stream. These iterator adaptors can be used on any input stream including istrstreams and ifstreams.
A common use of iterator adaptors is in some of the STL algorithms
that use iterator ranges.
For example, the copy() functions moves the objects
in the range defined by the first two arguments to the third.
vector
// some code to put stuff in v1
copy(v1.begin(),v1.end(),v2.begin());
Starting at the first element in v1, copy it to the
first element in v2.
Increment both pointers and do it again until we reach the
end pointer on v1.
One thing you have to be careful about when
using the copy() function this way
is that it assumes that the destination has the space needed
to put the source elements in.
A little later we will see a way to make this use push_back()
to add elements to the destination.
Now if we could use standard output as an iterator, we could use
copy() to send the elements of an array to the output.
The standard way to do this is
for(int i=0;i < v.size; i++)
cout << v[i];
We can use an ostream_iterator to convert the standard output
operator to an iterator.
ostream_iterator
is how you declare an adaptor.
The second type to the template can usually be left out.
It refers to the memory model needed for compilers that have
memory models.
The template should default to ptrdiff_t.
If it complains, use ptrdiff_t.
Here is an example of using copy to write out a vector.
copy(v.begin(),v.end(), ostream_iterator
The second argument to the adaptor is a separator that is
printed after each element.
Input can be done as well.
copy(istream_iterator
This still presents the problem of making sure there is enough space
in the vector before running this.
Another kind of adaptor can be used to fix this.
copy(istream_iterator
istream_iterator
back_insert_iterator
The back insert adaptor turns the dereference calls into calls to
the push_back() method on vectors.
This example really shows the ability to use classes and operator overloading to do things that would be very difficult in C.
int zero() {return 0;};
generate(v.begin(),v.end(),zero);