I am new to the C++ language. I have been starting to use vectors, and have noticed that in all of the code I see to iterate though a vector via indices, the first parameter of the for
loop is always something based on the vector. In Java I might do something like this with an ArrayList:
for(int i=0; i < vector.size(); i++){
vector[i].doSomething();
}
Is there a reason I don't see this in C++? Is it bad practice?
With STL, programmers use
iterators
for traversing through containers, since iterator is an abstract concept, implemented in all standard containers. For example,std::list
has nooperator []
at all.I was surprised nobody mentioned that iterating through an array with an integer index makes it easy for you to write faulty code by subscripting an array with the wrong index. For example, if you have nested loops using
i
andj
as indices, you might incorrectly subscript an array withj
rather thani
and thus introduce a fault into the program.In contrast, the other forms listed here, namely the range based
for
loop, and iterators, are a lot less error prone. The language's semantics and the compiler's type checking mechanism will prevent you from accidentally accessing an array using the wrong index.There's a couple of strong reasons to use iterators, some of which are mentioned here:
Switching containers later doesn't invalidate your code.
i.e., if you go from a std::vector to a std::list, or std::set, you can't use numerical indices to get at your contained value. Using an iterator is still valid.
Runtime catching of invalid iteration
If you modify your container in the middle of your loop, the next time you use your iterator it will throw an invalid iterator exception.
The reason why you don't see such practice is quite subjective and cannot have a definite answer, because I have seen many of the code which uses your mentioned way rather than
iterator
style code.Following can be reasons of people not considering
vector.size()
way of looping:size()
every time in the loop condition. However either it's a non-issue or it can be trivially fixedstd::for_each()
over thefor
loop itselfstd::vector
to other one (e.g.map
,list
) will also demand the change of the looping mechanism, because not every container supportsize()
style of loopingC++11 provides a good facility to move through the containers. That is called "range based for loop" (or "enhanced for loop" in Java).
With little code you can traverse through the full (mandatory!)
std::vector
:The cleanest way of iterating through a vector is via iterators:
or (equivalent to the above)
Prior to C++0x, you have to replace auto by the iterator type and use member functions instead of global functions begin and end.
This probably is what you have seen. Compared to the approach you mention, the advantage is that you do not heavily depend on the type of
vector
. If you changevector
to a different "collection-type" class, your code will probably still work. You can, however, do something similar in Java as well. There is not much difference conceptually; C++, however, uses templates to implement this (as compared to generics in Java); hence the approach will work for all types for whichbegin
andend
functions are defined, even for non-class types such as static arrays. See here: How does the range-based for work for plain arrays?No. It is not a bad practice, but it renders your code certain flexibility.
Usually, pre-C++11 the code for iterating over container elements uses iterators, something like:
This is because it makes the code more flexible.
All standard library containers support and provide iterators and given that if at a later point of development you need to switch another container then this code does not need to be changed.
Note: Writing code which works with every possible standard library container is not as easily possible as it might seemingly seem to be.