使用 'for' 循环遍历C++向量

Iterate through a C++ Vector using a 'for' loop

本文关键字:C++ 向量 遍历 for 使用 循环      更新时间:2023-10-16

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

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

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

你没有看到这种做法的原因是相当主观的,不能有一个明确的答案,因为我见过许多使用你提到的方式而不是iterator样式代码的代码。

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

    偏执地每次
  1. 在循环中调用size()条件。但是,要么这不是问题,要么可以微不足道固定
  2. 更喜欢std::for_each()而不是for循环本身
  3. 稍后将容器从std::vector更改为另一个容器(例如 maplist)也将要求改变循环机制,因为不是每个容器都支持size()循环风格

C++11为通过集装箱提供了良好的设施。这被称为"基于范围的 for loop"(或 Java 中的"增强的 for loop")。

只需很少的代码,您就可以遍历完整(强制性的! std::vector

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

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

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

或(等同于上述)

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

在 C++0x 之前,必须将 auto 替换为迭代器类型,并使用成员函数而不是全局函数开始和结束。

这可能就是你所看到的。与您提到的方法相比,优点是您不严重依赖vector的类型。如果将vector更改为其他"集合类型"类,则代码可能仍有效。但是,您也可以在Java中执行类似操作。在概念上没有太大区别;但是,C++使用模板来实现这一点(与 Java 中的泛型相比);因此,该方法适用于定义了beginend函数的所有类型,甚至适用于非类类型,如静态数组。请参阅此处: 基于范围的 for 如何适用于普通数组?

有什么理由我在C++看不到这个吗?这是不好的做法吗?

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

通常,在 C++11 之前,用于迭代容器元素的代码使用迭代器,如下所示:

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

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

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

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

正确的方法是:

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();
}

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

使用 auto 和 for 循环迭代向量

vector<int> vec = {1,2,3,4,5}
for(auto itr : vec)
    cout << itr << " ";

输出:

1 2 3 4 5

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

迭代向量并打印其值的正确方法如下:

#include<vector>
// declare the vector of type int
vector<int> v;
// insert elements in the vector
for (unsigned int i = 0; i < 5; ++i){
    v.push_back(i);
}
// print those elements
for (auto it = v.begin(); it != v.end(); ++it){
    std::cout << *it << std::endl;
}

但至少在目前的情况下,使用基于范围的 for 循环会更好:

for (auto x: v) std::cout << x << "n";(您也可以在auto后添加&,以使x引用元素而不是元素的副本。然后,它与上述基于迭代器的方法非常相似,但更易于读写。

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

稍后切换容器不会使代码失效。

也就是说,如果你从一个std::

vector转到一个std::list,或者std::set,你不能使用数字索引来获得你所包含的值。 使用迭代器仍然有效。

无效迭代的运行时捕获

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

这是一种在 vector 中迭代和打印值的更简单方法。

for(int x: A) // for integer x in vector A
    cout<< x <<" "; 
使用

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

我很

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

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

不要忘记

具有常量正确性的示例 - 循环可以修改元素吗? 这里的许多示例没有,应该使用 cont 迭代器。 在这里我们假设

class T {
  public:
    T (double d) : _d { d } {}
    void doSomething () const { cout << _d << endl; return; }
    void changeSomething ()   { ++_d; return; }
  private:
    double _d;
};
vector<T> v;
// ...
for (const auto iter = v.cbegin(); iter != v.cend(); ++iter) {
    iter->doSomething();
}

另请注意,使用 C++11 表示法时,默认值是复制元素。 使用引用来避免这种情况,和/或允许修改原始元素:

vector<T> v;
// ...
for (auto t : v) {
    t.changeSomething(); // changes local t, but not element of v
    t.doSomething();
}
for (auto& t : v) {      // reference avoids copying element
    t.changeSomething(); // changes element of v
    t.doSomething();
}
for (const auto& t : v) { // reference avoids copying element
    t.doSomething();      // element can not be changed
}
 //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;
int main()
{
    int n;
    int input;
    vector<int> p1;
    vector<int> ::iterator it;
    cout << "Enter the number of elements you want to insert" << endl;
    cin >> n;
    for (int i = 0;i < n;i++)
    {
        cin >> input;
        p1.push_back(input);
    }
    for(it=p1.begin();it!=p1.end();it++)
    {
        cout << *it << endl;
    }
      //Iterating in vector through iterator it
    return 0;
}

迭代器的传统形式

如果您使用

std::vector<std::reference_wrapper<std::string>> names{ };

别忘了,当你在for循环中使用auto时,也要使用get,像这样:

for (auto element in : names)
{
    element.get()//do something
}