开发者

To iterate or to use a counter, that is the question

Whenever someone starts using the STL and they have a vector, you usually see:

vector<int> vec ;

//... code ...

for( vector<int>::iter开发者_Python百科ator iter = vec.begin() ;
     iter != vec.end() ;
     ++iter )
{
  // do stuff
}

I just find that whole vector<int>::iterator syntax sickitating. I know you can typedef vector<int>::iterator VecIterInt, and that is slightly better..

But the question is, what's wrong with good ol':

for( int i = 0 ; i < vec.size() ; i++ )
{
  // code
}


When you use index to perform essentially sequential access to a container (std::vector or anything else) you are imposing the random-access requirement onto the underlying data structure, when in fact you don't need this kind of access in your algorithm. Random-access requirement is pretty strong requirement, compared to a significantly weaker requirement of sequential access. Imposing the stronger requirement without a good reason is a major design error.

So the correct answer to your question is: use sequential (iterator) access whenever you can, use random (index) access only when you absolutely have to. Try to avoid index access whenever possible.

If your algorithm critically relies on the container being random-accessible, it becomes the external requirement of the algorithm. In this case you can use index access without any reservations. However, if it is possible to implement the same algorithm using iterators only, it is a good practice to stick to iterators only, i.e. exclusively rely on sequential access.

Of course, the above rule, while true, only makes sense in the code is generic to a certain degree. If some other portion of the code is so specific, that you know for sure that the data structure you are working with is a std::vector and will always be a std::vector, then the access method no longer matters. Use whatever you prefer. However, I would still avoid index access in situations when sequential access is perfectly sufficient.


Well when it comes to std::vector I think using the subscript operator in a loop is just fine, and probably about the same in terms of performance. The advantage to using an iterator comes when you want to use the vector with other std functions like in <algorithms>.


I don't think my argument is very strong but I almost always use the iterator version.

typedef std::vector<int> MyIndexes; // or whatever
MyIndexes indexes;
for (Something::iterator iter = indexes.begin(); iter != indexes.end(); ++iter);

Now if I have to change the vector to list or something similar I only have to change my typedef. This was useful on couple of occasions. auto keyword will make this better but I can't wait for C++0x for loop :)


As a general rule, not limiting ourselves to std::vector, iterators can be more efficient than indexing, since they have access to the internals of the collection and knows the state of the iteration.

In the case of std::vector, the iterator in the loop will reduce to pointer arithmetic when compiled with optimizations on. A clever compiler might be able to do the same with a counter loop, and there shouldn't be any difference in performance, but in general (for more complex collections) you should assume that an iterator will give you the fastest code.


With C++0x you won't have this dilemma. You'll use the new for which is actually foreach :)


I would recommend using the iterators simply because they are more generic. If later in your development cycle, you decide that a std::list<> would be better then a std::vector<> (perhaps you find out you have a performance problem because you need to pop elements from the head of the container), it will take less effort to change the iterator version.


If you create, use, and destroy a vector all within a single scope, there's not much benefit to using the iterator abstraction, but for richer containers, e.g., std::map, the overhead becomes worth it.

Iterators are great for generic programming. None of the machinery and bookkeeping obscure the code's intent, and you get great flexibility. Wanna copy one vector to another?

std::vector<int> v1, v2;
// ...

std::copy(v1.begin(), v1.end(), v2.begin());

How about to std::cout instead?

std::ostream_iterator<int> output(std::cout, " ");
std::copy(v1.begin(), v2.end(), output);

Iterators allow a single template to handle all sorts of cases.


I normally use a for loop for vectors - to me it is the canonical way of dealing with such things. And don't let anyone tell you iterators are faster - see What's faster, iterating an STL vector with vector::iterator or with at()?.

Also, one thing that extreme proponents of iterators and constructs such as foreach tend to ignore is that with a for-loop you actually have an integer index you can do things with other than access the collection elements, which can be very useful in all sorts of situations.

However, if I were to use an STL algorithm, or iterate over an other container type, such as std::map, I would of course use iterators.


Personaly, I prefer using an iterator for iterating. Shows the intent better than accessing by index, IMHO, and the coding idiom is the same for different containers.


Always in two minds about this. To the computer it doesn't matter, the compiler is big enough to look after itself and will end up generating equally good code for either case.

But what about the programmer?

Seeing for(int i=0;i<blah.size();i++) my eye immediately reads this as "loop over all elements".
While seeing:

typedef std::vector<int> MyIndexes; 
MyIndexes indexes;
for (Something::iterator iter = indexes.begin(); iter != indexes.end(); ++iter);

I have to careful read each line to check that you aren't doing something tricky.

On the other hand seeing the for() loop makes me worry that the code is written by a C programmer who knows nothing about the STL and is going to be horribly designed.


The flagged answer is wrong. There's no 'access' going on is this code at all.

The real answer should be no difference in this case unless the vector implementation is exceedingly incompetent, except using an iterator will be slightly slower.

Iterator for something that is stored in an array is quite a silly concept. It's just done that way because some other cases can't be accesed directly as an array, so to be more general they encourage iterator use for everything. Even though iterators are ridiculously cumbersome, and only make sense to use in a few special cases.


IMO, using either one is pretty much missing the point. Iterating over a collection should be done in an algorithm. Quite a bit of the time, an existing algorithm will do the job quite nicely, but when it doesn't, you're generally best off writing something yourself that's actually usable as a generic algorithm.

This is one of those rather strange cases where the generic code is often simpler (and easier to write) than the most specialized code. In particular, instead of the iterator being something like std::map<std::string, my_type>::iterator, the iterator type is a template parameter with whatever name you find convenient:

template <class iter>
void my_algorithm(iter a, iter b) { 
    for (iter i=a; i!=b; ++i) 
        do_stuff(*i); 
}

But I'll repeat: quite a bit of the time, you can use an existing algorithm. For the case you cited, it appears that std::for_each (or Boost FOR_EACH) will probably work quite nicely.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜