使用“ for”循环遍历C ++ Vector


140

我是C ++语言的新手。我已经开始使用向量,并且注意到在我看到的所有代码中都通过索引迭代向量,for循环的第一个参数始终是基于向量的。在Java中,我可以使用ArrayList做类似的事情:

for(int i=0; i < vector.size(); i++){
   vector[i].doSomething();
}

有没有我在C ++中看不到的原因?这是不好的做法吗?


1
for循环不是一个函数,因此它没有参数(或您要传入的参数)。您是说类似的意思std::vector<int>::size_type i = 0;,还是std::vector<int>::iterator it = vector.begin();呢?
克里斯,2012年

确实,我看到的所有示例都是这样写的。
弗林2012年

4
在Java中,我希望使用for-each循环或使用迭代器。尽管语法略有不同,但与C ++几乎相同。
杰西·古德


10
此处的大多数答案都错误地将Q假定为:迭代的最佳/最短方法是std::vector什么?,这里要问的实际Q是:有什么理由在C ++中看不到吗?这是不好的做法吗?aka 为什么我总是在C ++中看到在迭代时使用迭代器的代码std::vector
Alok保存

Answers:


92

有什么原因我在C ++中看不到吗?这是不好的做法吗?

不会。这不是一个坏习惯,但是以下方法使您的代码具有一定的灵活性

通常,在C ++ 11之前的版本中,用于遍历容器元素的代码使用迭代器,例如:

std::vector<int>::iterator it = vector.begin();

这是因为它使代码更加灵活。

所有标准库容器都支持并提供迭代器。如果在以后的开发中需要切换到另一个容器,则无需更改此代码。

注意:编写适用于所有可能的标准库容器的代码并不像看起来那样容易。


25
谁能解释一下为什么在这种特殊情况/代码段中,您建议迭代器优先使用索引?您所说的“灵活性”是什么?就我个人而言,我不喜欢迭代器,它们会膨胀代码-只需键入更多字符即可获得相同的效果。特别是如果您不能使用auto
紫罗兰色长颈鹿

8
@VioletGiraffe:在使用迭代器时,在某些情况下(如空范围)很难出错,并且代码更加冗长,当然这是一个问题或看法和选择,因此可以无休止地进行辩论。
Alok保存

9
为什么只显示如何声明迭代器,而不显示如何使用它来执行循环...?
underscore_d

116

您没有看到这种做法的原因是很主观的,无法给出明确的答案,因为我已经看到许多使用您提到的方式而不是iterator样式代码的代码。

以下是人们不考虑vector.size()循环方式的原因:

  1. size()每次在循环条件下都调用感到偏执。但是,它不是一个问题,也可以被简单修复。
  2. 优先std::for_each()for循环本身
  3. 后来从改变容器std::vector到另一个(例如 maplist)也将需求循环机制的变化,因为不是每个容器支撑,size()风格的循环

C ++ 11提供了在容器之间移动的良好工具。这称为“基于范围的循环”(或Java中的“增强的循环”)。

只需少量代码,您就可以遍历整个代码(强制性!)std::vector

vector<int> vi;
...
for(int i : vi) 
  cout << "i = " << i << endl;

12
只是要注意基于范围的for循环的一个小缺点:您不能将它与一起使用#pragma omp parallel for
liborm 2014年

2
我喜欢精简版,因为要读取的代码更少。一旦您进行了心理上的调整,它就会变得更容易理解,并且bug更加突出。当发生非标准迭代时,这也变得更加明显,因为其中有更多的代码块。
代码憎恶者

87

遍历向量的最干净方法是通过迭代器:

for (auto it = begin (vector); it != end (vector); ++it) {
    it->doSomething ();
}

或(相当于以上)

for (auto & element : vector) {
    element.doSomething ();
}

在C ++ 0x之前,您必须用迭代器类型替换auto并使用成员函数而不是全局函数begin和end。

这可能就是您所看到的。与您提到的方法相比,优点是您不会严重依赖的类型vector。如果您更改vector为其他“集合类型”类,则您的代码可能仍将起作用。但是,您也可以在Java中执行类似的操作。概念上并没有太大区别;但是,C ++使用模板来实现此目的(与Java中的泛型相比);因此方法将用于所有类型的工作beginend功能而定义的,即使对于非类类型,如静态数组。请参阅此处:基于范围的纯数组如何工作?


5
自动,免费的开始/结束也是C ++ 11。同样,在许多情况下,您应该使用++ it,而不是++。
ForEveR

你是对的。但是,实施beginend是一脉相承的。
JohnB

@JohnB是一个不止一个内衬,因为它也适用于固定大小的数组。auto另一方面会非常棘手。
juanchopanza 2012年

如果只需要它作为矢量,则它是单线的。
JohnB

尽管如此,第一个示例还是令人误解,因为它不能在C ++ 03中工作,而您的措辞表明确实如此。
juanchopanza 2012年

35

正确的方法是:

for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) {
    it->doSomething();
 }

其中T是向量内的类的类型。例如,如果该类是CActivity,则只需编写CActivity而不是T。

这种类型的方法将适用于每个STL(不仅是向量,这要好一些)。

如果仍然要使用索引,则方法是:

for(std::vector<T>::size_type i = 0; i != v.size(); i++) {
    v[i].doSomething();
}

并不std::vector<T>::size_type总是size_t吗?这就是我一直使用的类型。
紫罗兰色长颈鹿2012年

1
@VioletGiraffe我可以肯定你是对的(还没有真正检查过),但是使用std :: vector <T> :: size_type是更好的做法。
DiGMi 2012年

8

使用迭代器有两个很强的理由,这里提到其中一些:

稍后切换容器不会使您的代码无效。

即,如果您从std :: vector转到std :: list或std :: set,则无法使用数字索引来获得所包含的值。使用迭代器仍然有效。

运行时捕获无效迭代

如果在循环的中间修改容器,则下次使用迭代器时,它将引发无效的迭代器异常。


1
您能否指出一些我们用示例代码解释以上几点的文章/帖子?会很好!或者,如果您可以添加一个:)
Anu,

5

令我惊讶的是,没有人提到遍历具有整数索引的数组,可以通过为数组下标错误的索引来简化编写错误代码的过程。例如,如果您有使用ij作为索引的嵌套循环,则可能会错误地用j而不是用而不是数组下标i,从而将错误引入程序中。

相反,这里列出的其他形式,即基于范围的for循环和迭代器,则不易出错。语言的语义和编译器的类型检查机制将防止您意外地使用错误的索引访问数组。


4

使用STL,程序员可以iterators遍历容器,因为迭代器是一个抽象概念,已在所有标准容器中实现。例如,std::list完全没有operator []


3

使用auto运算符确实使它易于使用,因为您不必担心数据类型和向量的大小或任何其他数据结构

使用auto和for循环迭代向量

vector<int> vec = {1,2,3,4,5}

for(auto itr : vec)
    cout << itr << " ";

输出:

1 2 3 4 5

您也可以使用此方法来迭代集和列表。使用自动会自动检测模板中使用的数据类型并让您使用它。因此,即使我们使用vectorof stringchar相同的语法也可以正常工作


1

迭代循环并打印其值的正确方法如下:

#include<vector>

//declare the vector of type int
vector<int> v;

//insert the 5 element in the vector
for ( unsigned int i = 0; i < 5; i++){
    v.push_back(i);
}

//print those element
for (auto it = 0; it < v.end(); i++){
    std::cout << *it << std::endl;
}


0
 //different declaration type
    vector<int>v;  
    vector<int>v2(5,30); //size is 5 and fill up with 30
    vector<int>v3={10,20,30};
    
    //From C++11 and onwards
    for(auto itr:v2)
        cout<<"\n"<<itr;
     
     //(pre c++11)   
    for(auto itr=v3.begin(); itr !=v3.end(); itr++)
        cout<<"\n"<<*itr;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.