Hello again! In this video, we're going to look at loops and iterators.
There's actually several different types of its iterators, and we're going to go through those, and look
at how to use them with loops over containers.
The first one is the const iterator.
So this is used to access the elements in a container, but without being able to modify them.
The name is const underscore iterator. Again, it's a member of the container class.
So if we have a const iterator to a string, it'll be string double colon const underscore iterator.
And we can use this just like a regular iterator, so we can call, begin and compare the results to
end and increment to step through the elements in a container.
There's two uses for this.
One is if you simply want to iterate and make sure that you don't accidentally change anything. The other
one is, if you have a const container, then you need to use a const iterator with it.
And the most common circumstance is if you're in a function body and you've been passed a const
container. You'll remember that's the best way generally to pass large objects is by reference to const.
So in that case, you'll have a const container and you need to use a const iterator with that.
A slightly more complex one is the reverse iterator.
So this will actually iterate backwards through a container, so we have string double colon reverse
iterator. And this literally does work backwards.
So when you call begin, or rather rbegin, this will return a reverse iterator.
And this will be pointing to the last element in the string.
So if we have the string "hello", rbegin will return an iterator which points to the letter 'o'.
And then if we increment that, it'll move backwards through the string, so it'll then be pointing
to the last letter 'l'.
Then if we keep on incrementing, it goes [to] the next 'l', then the 'e' and then the 'H'.
And if we increment it again, we go past the first element in the string and then we get an iterator
which is equal to the result of calling rend.
So the actual form of the loop looks exactly the same, but the results are backwards.
So.
When we begin, we start at the back of the string, then we increment to go backwards.
And then when we go past the front of the string, we stop the loop.
So here we are, we've got our string hello. Then we're doing this with a normal iterator, just to remind
ourselves what that does.
And then we're going to do this with a reverse iterator.
So that's the code we had on the slide.
So what happens?
So there we are, the normal iterator prints out the characters in the normal order, going left to right.
And the reverse iterator prints them out in reverse order, starting with the last element and ending
with the first.
OK.
C++ now provides member functions, so we can use const iterators with auto. So we can call cbegin and cend
those will return const iterators.
So if we want to loop through our string without altering it, then we call cbegin instead of begin
and cend instead of end. And the compiler will deduce the type automatically.
It'll see that this returns a const iterator, so it needs to be a const iterator to string.
And then there's also a couple of other member functions. If you want a const reverse iterator.
So if you want to go backwards without changing anything you call crbegin and crend,
and the compiler will deduce this as a const reverse iterator.
So that's also a new type in C++
11.
So let's just remind ourselves so we have the standard iterator with begin and end.
We have the const iterator with cbegin and cend. We have the reverse iterator with rbegin and
rend.
And then we have the const reverse iterator with crbegin and crend.
So there we are, the iterator and the const iterator, all provide the - well, I'm sure you can
work out what's going on!
So there we are, we have four different types of iterator for iterating through a container.
Also in C++ 11, there are non-member begin and end functions. So you can call these without having
to write a member function call.
And I would say that these are more readable than the alternatives with a member function.
So instead of saying str dot begin, you'd say begin with str as the argument. And then end with the
string as the argument. And apart from that, it works exactly the same.
The big advantage this is they also work with built-in arrays.
Of course, arrays are built-in types, so they don't have member functions.
So you can iterate over arrays, just like you can do with strings or vectors or other library containers.
So you call begin with the array as the argument and end with the array as the argument and then you can
iterate through them. And then C++ 14.
They also added the missing versions, so that's pretty much C++ 14, really, which is just filling in all the
gaps from C++ 11 and adding a few "nice to haves."
So we have see cbegin for const iterators, rbegin for reverse iterators and crbegin for const reverse
iterators.
And we can try that out. So we've got an array here.
Then we have all these different forms of iterators, using the global calls. Because we can't use
the member functions because arrays don't have member functions.
And there we are, that's what we would expect.
And finally, we're going to look at range-for loops. so this is a special, concise syntax for
iterating over containers.
So if you are just doing the standard loop where you start at the beginning and look at each element
at once and stop when you get to the end, this is a much shorter way of writing it.
So we're using auto, then we have the container and then before the container, we have this colon
and then we have some variable. And this variable is going to represents each element on the container
on the iteration.
So the first time through that's going to be the first element of the second time to it's going to be
the second element and so on.
And when the compiler does that, it's actually going to generate the code for the loop.
So to get this element, it will dereference the iterator and then copy the
dereferenced iterator into this variable. So this is actually going to be a copy of the element.
So if we try to make any changes to the element in here, it's not going to affect the container.
So these two forms are exactly equivalent.
This is just a shorthand for that.
So what happens if we do want to be able to modify the elements in the loop?
We can do that by slipping in a reference after the auto.
So you remember that if we use auto, it'll always give the actual type and ignore any const or reference.
If we add a reference to the auto, then it's going to be a reference to the element type.
So if we have a vector of int, then el is going to be a reference to int.
And then the actual element in here is going to be a reference to the element in the vector.
So when we write this code, the compiler will generate this. So this looks a bit strange, but
it does work.
So it dereferences the iterator to get the element value and then it initializes a reference to
that element.
So this is going to be a reference to the element.
So when we modify this reference, it will also modify the element.
OK.
So let's try that out.
We've got a vector, and then we're going to use a range for loop to iterate over that.
So this is going to print every element in the vector.
This is just equivalent to a loop which has "for auto iterator equals vec dot begin, not equals end,
"plus plus iterator."
Then we're going to use the reference form, so this element is going to be a reference to the element
in the vector.
So this is going to add two to each element and then we're going to do the first loop again to see
what we've actually got in the vector after doing that operation.
So we had one two three four before.
So we should, we hope, get three four five six.
Let's see what happens.
There we are, so the first loop puts out the elements in the vector.
So these are just copies of the vector elements.
Then in the second loop, we have references to the element.
So we're adding two to each element.
And then in the final loop, we're printing out the elements again and we do get three four five
six.
So these range-for loops are very useful.
You know, a lot of times you are just watching a loop where you go over all the elements.
You may be doing something a bit more complex than printing them out, but it is something that you do a lot
of in C++. And this will make your code a lot shorter and easier to read.
So I'd recommend that you use range-for loops wh... (Argh!)
So when should you use range-for loops?
I recommend that you use them all the time. Whenever it's possible.
Obviously, there are some situations where you can't do that. If you only want to look at some of the
elements in the container, if you want to go in a different order or skip out elements.
And also, if you add or remove elements from the container, then that won't work properly.
So in that case, you need to use the traditional loop and you may need to tweak it a bit to make it
do what you want. But otherwise, if you are just going through all the elements in order and you're
not actually modifying the container, then range for loops are really useful and they are one of the
best features of C++11.
I would say.
Okay, so that's it for this video.
I'll see you next time.
But meanwhile, keep coding!