C++: Proper way to iterate over STL containers

后端 未结 8 1398
灰色年华
灰色年华 2021-01-06 04:55

In my game engine project, I make extensive use of the STL, mostly of the std::string and std::vector classes.

In many cases, I have to ite

相关标签:
8条回答
  • 2021-01-06 05:38

    You're doing it OK for vectors, although that doesn't translate into the right way for other containers.

    The more general way is

    for(std::vector<foo>::const_iterator i = theContainer.begin(); i != theContainer.end; ++i)
    

    which is more typing than I really like, but will become a lot more reasonable with the redefinition of auto in the forthcoming Standard. This will work on all standard containers. Note that you refer to the individual foo as *i, and use &*i if you want its address.

    In your loop, .size() is executed every time. However, it's constant time (Standard, 23.1/5) for all standard containers, so it won't slow you down much if at all. Addition: the Standard says "should" have constant complexity, so a particularly bad implementation could make it not constant. If you're using such a bad implementation, you've got other performance issues to worry about.

    0 讨论(0)
  • 2021-01-06 05:42

    C++11 has a new container aware for loop syntax that can be used if your compiler supports the new standard.

    #include <iostream>
    #include <vector>
    #include <string>
    
    using namespace std;
    
    int main() 
    {
        vector<string> vs;
        vs.push_back("One");
        vs.push_back("Two");
        vs.push_back("Three");
    
        for (const auto &s : vs)
        {
            cout << s << endl;
        }
    
        return 0;
    }
    
    0 讨论(0)
  • 2021-01-06 05:45

    No, this is not the correct way to do it. For a ::std::vector or a ::std::string it works fine, but the problem is that if you ever use anything else, it won't work so well. Additionally, it isn't idiomatic.

    And, to answer your other question... The size function is probably inline. This means it likely just fetches a value from the internals of ::std::string or ::std::vector. The compiler will optimize this away and only fetch it once in most cases.

    But, here is the idiomatic way:

    for (::std::vector<Foo>::iterator i = theContainer.begin();
         i != theContainer.end();
         ++i)
    {
        Foo &cur_element = *i;
        // Do stuff
    }
    

    The ++i is very important. Again, for ::std:vector or ::std::string where the iterator is basically a pointer, it's not so important. But for more complicated data structures it is. i++ has to make a copy and create a temporary because the old value needs to stick around. ++i has no such issue. Get into the habit of always using ++i unless you have a compelling reason not to.

    Lastly, theContainer.end() will also be generally optimized out of existence. But you can force things to be a little better by doing this:

    const ::std::vector<Foo>::iterator theEnd = theContainer.end();
    
    for (::std::vector<Foo>::iterator i = theContainer.begin(); i != theEnd; ++i)
    {
        Foo &cur_element = *i;
        // Do stuff
    }
    

    Of course, C++0x simplifies all of this considerably with a new syntax for for loops:

    for (Foo &i: theContainer)
    {
         // Do stuff with i
    }
    

    These will work on standard fix-sized arrays as well as any type that defines begin and end to return iterator-like things.

    0 讨论(0)
  • 2021-01-06 05:47

    Native for-loop (especially index-based) - it's C-way, not C++-way.

    Use BOOST_FOREACH for loops.

    Compare, for container of integers:

    typedef theContainer::const_iterator It;
    for( It it = theContainer.begin(); it != theContainer.end(); ++it ) {
        std::cout << *it << std::endl;
    }
    

    and

    BOOST_FOREACH ( int i, theContainer ) {
        std::cout << i << std::endl;
    }
    

    But this is not perfect way. If you can do your work without loop - you MUST do it without loop. For example, with algorithms and Boost.Phoenix:

    boost::range::for_each( theContainer, std::cout << arg1 << std::endl );
    

    I understand that these solutions bring additional dependencies in your code, but Boost is 'must-have' for modern C++.

    0 讨论(0)
  • 2021-01-06 05:50

    STL containers support Iterators

    vector<int> v;
    for (vector<int>::iterator it = v.begin(); it!=v.end(); ++it) {
        cout << *it << endl;
    }
    

    size() would be re-computed every iteration.

    0 讨论(0)
  • 2021-01-06 05:53

    Usually the right way to "iterate" over a container is using "iterators". Something like

    string myStr = "hello";
    for(string::iterator i = myStr.begin(); i != myStr.end(); ++i){
        cout << "Current character: " << *i << endl;
    }
    

    Of course, if you aren't going to modify each element, it's best to use string::const_iterator.

    And yes, size() gets called every time, and it's O(n), so in many cases the performance loss will be noticeable and it's O(1), but it's a good practice to calculate the size prior to the loop than calling size every time.

    0 讨论(0)
提交回复
热议问题