使用'for'循环

我不熟悉c++语言。我已经开始使用向量,并注意到在我看到的通过索引遍历向量的所有代码中,for循环的第一个参数总是基于向量的值。在Java中,我可以对数组列表做这样的事情:

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

我在c++中看不到这个的原因是什么呢?这是不好的做法吗?

910147 次浏览

遍历vector最简洁的方法是通过迭代器:

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函数的所有类型,即使是非类类型,如静态数组。看这里:基于范围的for如何用于普通数组?

正确的做法是:

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

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

不。这不是一个坏的做法,但下面的方法使你的代码某些灵活性

通常,在c++ 11之前,迭代容器元素的代码使用迭代器,类似于:

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

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

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

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

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

为什么你没有看到这样的做法是相当主观的,无法有一个明确的答案,因为我已经看到许多代码使用你提到的方式,而不是iterator风格的代码。

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

  1. 每次在循环中都要调用size() 条件。然而,这要么不是问题,要么是小事 李固定< / >
  2. 优先选择std::for_each()而不是for循环本身
  3. 稍后将容器从std::vector更改为其他容器(例如: maplist)也将要求改变循环机制, 因为不是每个容器都支持size()风格的循环

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

用很少的代码,你可以遍历完整的(强制的!)std::vector:

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

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

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

例如,如果从std::vector转换为std::list或std::set,则不能使用数值索引来获取包含的值。使用迭代器仍然有效。

无效迭代的运行时捕获

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

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

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

使用auto操作符真的很容易使用,因为人们不必担心数据类型和向量或任何其他数据结构的大小

使用auto和for循环迭代vector

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


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

输出:

1 2 3 4 5
你也可以使用这个方法来迭代集和列表。使用汽车自动检测模板中使用的数据类型,并允许您使用它。 因此,即使我们有stringcharvector,相同的语法也可以正常工作

在vector上迭代并打印其值的正确方法如下:

#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成为对元素的引用,而不是它们的副本。它与上面的基于迭代器的方法非常相似,但更容易读写。

这是一个更简单的方法来迭代和打印值在向量。

for(int x: A) // for integer x in vector A
cout<< x <<" ";
 //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;

如果你使用

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

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

for (auto element in : names)
{
element.get()//do something
}
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;
}

迭代器的常规形式

不要忘记具有const正确性的例子-循环可以修改元素。这里的许多示例都没有这样做,应该使用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
}