Professional C__ - Marc Gregoire [195]
for (auto& str : stringVector) {
str.append(" there");
}
Code snippet from VectorIterators\AccessingFields.cpp
const_iterator
The normal iterator is read/write. However, if you call begin() and end() on a const object, you receive a const_iterator. The const_iterator is read-only; you cannot modify the elements. An iterator can always be converted to a const_iterator, so it’s always safe to write something like this:
vector However, a const_iterator cannot be converted to an iterator. If myVector is const, the following line doesn’t compile: vector If you do not need to modify the elements of a vector, you should use a const_iterator. This rule will make it easier to guarantee correctness of your code and allows compilers to perform certain optimizations. When using the auto keyword of C++11, using const_iterators looks a bit different. Suppose you write the following code: vector for (auto iter = stringVector.begin(); iter != stringVector.end(); ++iter) { cout << *iter << endl; } Because of the auto keyword, the compiler will decide the type of the iter variable automatically and will make it a normal iterator, meaning that you can read and write to the iterator. If you want a read-only const_iterator in combination with using auto, then you need to use cbegin() and cend() instead of the normal begin() and end() as follows: vector for (auto iter = stringVector.cbegin(); iter != stringVector.cend(); ++iter) { cout << *iter << endl; } Code snippet from VectorIterators\ConstIterator.cpp Now the compiler will use the const_iterator as type for the variable iter because that’s what cbegin() returns. Iterator Safety Generally, iterators are about as safe as pointers: extremely insecure. For example, you can write code like this: vector auto it = intVector.end(); *it = 10; // BUG! it doesn't refer to a valid element. Code snippet from VectorIterators\IteratorSafety.cpp Recall that the iterator returned by end() is past the end of the vector. Trying to dereference it results in undefined behavior. However, the iterators themselves are not required to perform any verification. Remember that end() returns an iterator past the end of the container, not the iterator referring to the last element of the container. Another problem can occur if you use mismatched iterators. For example, the following code initializes an iterator from vectorTwo and tries to compare it to the end iterator for vectorOne. Needless to say, this loop will not do what you intended, and may never terminate. Dereferencing the iterator in the loop will likely produce undefined results. vector vector // Fill in the vectors. // BUG! Infinite loop for (auto it = vectorTwo.begin(); it != vectorOne.end(); ++it) { // Loop body } Code snippet from VectorIterators\IteratorSafety.cpp Some C++ runtimes, for example Microsoft Visual C++, will give an assertion error at run time for both of the preceding problems when running a debug build of your program. Other Iterator Operations The vector iterator is random access, which means that you can move it backward or forward, or jump around. For example, the following code eventually changes the fifth element (index 4) in the vector to the value 4: vector auto it = intVector.begin(); it += 5; --it; *it = 4; Code snippet from VectorIterators\IteratorOps.cpp Iterators versus Indexing Given that you can write a for loop that uses a simple index variable and the size() method to iterate over the elements of the vector, why should you bother using iterators? That’s a valid question, for which there are three main answers: Iterators allow you to insert and delete elements and sequences of elements at any point in the container. See the following “Adding and Removing Elements” section. Iterators allow you to use the STL