Hello again! In this video, we are going to look at stream iterators. The iterators we have used so far are
designed for use with containers. They are so-called random access iterators, which means you can
jump anywhere in a container. And it takes the same speed, whether you are going to the first element or the
one millionth element.
The standard library provides these iterators, which work on streams.
They are defined in the iterator header.
They can only work if all the data is of the same type.
There is an input stream
iterator,
istream_iterator, and an output stream
iterator, ostream_iterator.
These are templated types, so we need to instantiate them. And we need to give a parameter, which is
the type of the data that is going to be used with the stream.
These stream iterators have a very limited interface.
The only thing you can do with an output stream operator is to assign to it.
And that will cause the argument of the operation to be pushed onto the stream. With an input stream
[iterator], you can dereference it.
And that will give you the object, which is at the current position in the stream.
Or you can increment the iterator, and that will move the position market of the stream to the next object.
So it will be ready to read the next object.
The output stream operator does define these dereferencing and increment operators, but they do not
actually do anything.
They are just there, so you can write code that looks the same as any other code that uses iterators.
To create an output stream iterator, we need to bind it to an output stream, and we do that by
passing a stream object as the argument to the constructor.
So here we have an output stream iterator, which will work with ints, and it is bound to cout, to
the standard output.
Every time that we assign to this iterator, data is pushed onto the stream.
This is done using the left shift operator for the type.
So if you have a data type that does not support the left shift operator, then this will not compile.
So here we are assigning 7. Star oi equals 7.
The star is optional here,
but I have written it, so it looks the same as other iterator code. And that will actually cause the '7'
to appear on the output.
We can also provide a second argument, which is a delimiter, this is optional. Here,
I have provided a backslash 'n', the newline character.
This has to be a C string.
And then when we assign to this iterator, the argument of the assignment, the variable i, will be sent
to the stream, followed by a new line.
So in this loop: each iteration, the value of i will be sent to the output stream, followed by a
new line.
So we should get the numbers from 0 to 9, each displayed on a separate line.
So let's try this out.
Here's the code with the iterator header. The output stream iterator. And then the loop, which
assigns to the iterator.
I have also included the increment as well, which is -
Again, it does nothing, but it looks more consistent with iterator code.
So there we are, we get the numbers from 0 to 9, each followed by a newline character.
Creating an input stream operator is very similar.
We give the data type as the parameter, and we bind it to an input stream.
So this is an iterator, ii, which will read ints from the keyboard.
And then, when we dereference this, it will read an integer from the keyboard.
So let's try that.
There is our stream iterator, than we dereference it. And then we store the value that it gets in a int and then
we print out the value of that.
So we should get
whatever I type in here.
There we are, 42.
We can use a loop with an iterator to read multiple input.
The usual question arises. How do we know when to stop?
The way this works is actually rather clumsy.
When there is no more input to read, the iterator will be empty.
And then we have to compare the iteartor against an empty iterator.
So how do we get an empty iterator?
We do that by not binding it to any stream.
So here we have an iterator, which can read strings but is not bound to any input stream.
So in fact, we need two input iterators.
One to read the data and an empty one to compare against. And then we have a loop where we compare
the two iterators.
If there is some data to read, then this will not be empty.
So they will be not equal, and we go into the loop.
We can dereference the iterator. That will give us the data that we read from the keyboard, and then we
can store that somewhere. And then we can increment the iterator, so that it will move the stream marker
to the next position.
So that is now ready to read the next int from the keyboard.
And then this goes on.
And then when we read the end of input, this iterator will be empty.
And it will compare equal to this one, and the loop will end.
So let's try this out.
We have our two iterators, the one to read data and the empty one.
We have a vector where we are going to store all the data that we read.
Then we have our loop.
Is there any data in this iterator, or is it empty?
And then in the loop, we dereference the iterator to get the data that we have read, and we increment
the iterator.
So we are ready to read the next int. And this will continue, until we read the end of input.
And then, at the end, we have a range-for loop. And this will print out all the elements of the vector,
which should contain all the data that we have just read.
So I need to enter some numbers here...
End of input...
So there we are.
We do get all the numbers that I entered, in the order that I entered them.
So the control Z is of input and then the input iterator is equal to this empty iterator.
So what is the point of having these stream iterators?
Well, we can actually make the input-output statement a lot more concise.
We could actually do
this...
Now we have just a single line which will read in data.
So that is very concise. And I hope it still works...
Yes!
Always a worry when doing live coding! The other advantage is we can use these with standard algorithms,
which we are going to look at later.
(Sorry, more teasers!) These algorithms can do various things with containers such as transforming them,
and you can copy the results into another container.
So you just given an iteraator to the start of that container.
If instead you give an output stream operator, then the result of that operation will be displayed
on the screen.
So if you want to check that your program is doing the right thing, you just need to change an
iterator.
You do not need to write lots more code.
Okay, so that is it for this video.
I will see you next time.
But meanwhile, keep coding!