C ++:迭代STL容器的正确方法

时间:2011-02-07 18:48:51

标签: c++ stl iterator

在我的游戏引擎项目中,我广泛使用STL,主要是std::stringstd::vector类。

在许多情况下,我必须遍历它们。现在,我这样做的方式是:

for( unsigned int i = 0; i < theContainer.size(); i ++ )
{

}
  • 我是以正确的方式做的吗?
  • 如果没有,为什么,我该怎么做呢?

  • 使用此实现,每个循环周期是否真的执行了size()?性能损失是否可以忽略不计?

8 个答案:

答案 0 :(得分:9)

C ++ 11有一个新的容器感知循环语法,如果你的编译器支持新标准,可以使用它。

#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;
}

答案 1 :(得分:5)

您可能希望查看标准算法。

例如

vector<mylass> myvec;

// some code where you add elements to your vector

for_each(myvec.begin(), myvec.end(), do_something_with_a_vector_element);

其中do_something_with_a_vector_element是一个执行循环内容的函数

例如

void 
do_something_with_a_vector_element(const myclass& element)
{
 // I use my element here
}

有许多标准算法 - 请参阅http://www.cplusplus.com/reference/algorithm/ - 所以大多数事情都得到支持

答案 2 :(得分:4)

STL容器支持迭代器

vector<int> v;
for (vector<int>::iterator it = v.begin(); it!=v.end(); ++it) {
    cout << *it << endl;
}
每次迭代都会重新计算

size()。

答案 3 :(得分:2)

  • 对于随机访问容器,没错。
  • 但您可以使用迭代器。

    for (string::const_iterator it = theContainer.begin();
         it != theContainer.end(); ++it) {
        // do something with *it
    }
    
  • 在某些情况下,编译器可以优化.size()(或迭代器案例中的.end())调用(例如,仅const访问,函数为{{ 1}})。但不要依赖它。

答案 4 :(得分:1)

通常在容器上“迭代”的正确方法是使用“迭代器”。像

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

当然,如果您不打算修改每个元素,最好使用string::const_iterator

是的,size()每次被调用,并且它是O(n),因此在许多情况下性能损失将是显而易见的并且它是O(1),但它是一个在循环之前计算大小的好习惯,而不是每次调用大小。

答案 5 :(得分:1)

不,这不是正确的方法。对于::std::vector::std::string,它可以正常工作,但问题是,如果您使用其他任何东西,它将无法正常工作。此外,它不是惯用的。

并且,为了回答你的另一个问题...... size函数可能是内联的。这意味着它可能只是从::std::string::std::vector的内部获取值。编译器将对此进行优化,并且在大多数情况下仅获取一次。

但是,这是惯用的方式:

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

++i非常重要。同样,对于::std:vector::std::string,迭代器基本上是一个指针,它并不那么重要。但对于更复杂的数据结构而言。 i++必须制作副本并创建一个临时副本,因为旧值需要坚持下去。 ++i没有这样的问题。养成总是使用++i的习惯,除非你有令人信服的理由不这样做。

最后,theContainer.end()通常也会被优化。但是你可以通过这样做来强迫事情变得更好:

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
}

当然,C ++ 0x通过for循环的新语法大大简化了所有这些:

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

这些将适用于标准的修复大小的数组以及定义beginend以返回类似迭代器的任何类型。

答案 6 :(得分:1)

原生for循环(尤其是基于索引) - 它是C-way,而不是C ++方式。

使用BOOST_FOREACH进行循环。

比较,对于整数容器:

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

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

但这不是完美的方式。如果你可以在没有循环的情况下完成工作 - 你必须在没有循环的情况下完成。例如,使用算法和Boost.Phoenix:

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

我知道这些解决方案会在代码中带来额外的依赖关系,但Boost是现代C ++的“必备”。

答案 7 :(得分:0)

你对矢量没有问题,虽然这并没有转化为其他容器的正确方法。

更通用的方法是

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

这比我真正喜欢的打字更多,但在即将推出的标准中重新定义auto会变得更加合理。这适用于所有标准容器。请注意,您将个人foo称为*i,如果您想要其地址,请使用&*i

在循环中,每次都会执行.size()。但是,对于所有标准容器来说,它是恒定时间(标准,23.1 / 5),因此如果有的话,它不会减慢你的速度。另外:标准说“应该”具有不变的复杂性,因此特别糟糕的实现可能使其不稳定。如果你使用这么糟糕的实现,你还需要担心其他性能问题。