我刚接触C++语言。我开始使用向量,并注意到在所有遍历向量的代码中,通过索引迭代时for循环的第一个参数总是基于这个向量的。在Java中,我可能会像这样使用ArrayList:
for(int i=0; i < vector.size(); i++){
vector[i].doSomething();
}
我不明白为什么在C++中找不到这个东西?这是一种不好的实践吗?
我刚接触C++语言。我开始使用向量,并注意到在所有遍历向量的代码中,通过索引迭代时for循环的第一个参数总是基于这个向量的。在Java中,我可能会像这样使用ArrayList:
for(int i=0; i < vector.size(); i++){
vector[i].doSomething();
}
我不明白为什么在C++中找不到这个东西?这是一种不好的实践吗?
为什么你没有看到这样的做法,原因非常主观,不能有明确的答案,因为我见过很多代码都使用了您提到的方法而不是迭代器(iterator)风格的代码。
以下可能是人们不考虑使用 vector.size()
循环的原因:
size()
的情况感到担忧。但是这要么不是问题,要么可以轻松解决。std::for_each()
而不是 for
循环本身。std::vector
更改为其他容器(例如 map
、list
)也会要求更改循环机制,因为并非每个容器都支持 size()
风格的循环。C++11 提供了一种很好的遍历容器的方法,称为“范围 for 循环”(或 Java 中的 “增强型 for 循环”)。
只需少量代码,即可遍历整个(必须的!)std::vector
:
vector<int> vi;
...
for(int i : vi)
cout << "i = " << i << endl;
#pragma omp parallel for
一起使用。 - liborm使用迭代器是遍历向量最清晰的方法:
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中的泛型相比);因此,该方法适用于所有定义了begin和end函数的类型,甚至适用于静态数组等非类类型。请参见此处:How does the range-based for work for plain arrays?begin
和 end
只需要一行代码。 - JohnBbegin
和 end
,还有一个细节,它们需要 std::
前缀,除非你添加了 using namespace std;
。因此,许多人更喜欢使用 v.begin()
和 v.end()
而不是 begin(v)
等等。 - Max我在C++中为什么看不到这个方法?这是不好的编程习惯吗?
不是。这并不是不好的编程习惯,但以下方法可以使您的代码更加灵活。
通常,在C++11之前,迭代容器元素的代码使用迭代器,类似于:
std::vector<int>::iterator it = vector.begin();
这是因为它使代码更加灵活。
所有标准库容器都支持并提供迭代器。如果在开发的后期需要切换到另一个容器,那么这段代码无需更改。
注意:编写适用于所有可能的标准库容器的代码并不像看似那样简单。
auto
的情况下,请问能否解释一下?请问是否可以用迭代器来遍历容器,而不是通过数字索引来访问每个元素。这样做的好处是,避免了越界错误,并且在某些情况下可以提高代码的可读性和可维护性。另外,使用迭代器可以更轻松地处理不同类型的容器。您提到的“灵活性”指的是使用迭代器时,您可以以各种方式更改容器的内容,例如添加或删除元素,而无需更改循环本身。相比之下,如果使用索引,则必须小心计算边界和位置,以确保不会发生越界或其他错误。即使您无法使用auto
关键字,也可以使用C++11或更高版本中的范围for循环语法,以更简洁的方式使用迭代器。 - Violet Giraffe正确的做法是:
for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) {
it->doSomething();
}
T代表vector中的类的类型。例如,如果类是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
吗?这就是我一直使用的类型。 - Violet Giraffe使用auto操作符确实使得使用变得容易,因为不需要担心向量或任何其他数据结构的数据类型和大小
使用自动和for循环迭代向量
vector<int> vec = {1,2,3,4,5}
for(auto itr : vec)
cout << itr << " ";
输出:
1 2 3 4 5
你也可以使用这种方法迭代集合和列表。使用auto会自动检测模板中使用的数据类型并让您使用它。
因此,即使我们有一个vector
由string
或char
组成,相同的语法也将正常工作。
正确遍历向量并打印其值的方法如下:
#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
成为元素的引用而不是它们的副本。然后它与上述基于迭代器的方法非常相似,但更易于阅读和编写。)
auto
而不是int
? - user15072974it
是整数向量v
的迭代器,而不是整数,因此我们不能使用int
来声明it
,但是我们可以使用auto
让编译器自动推断it
的类型。您还可以在for循环中显式声明it
的类型,如vector<int>::iterator it
,例如for (vector<int>::iterator it = v.begin(); it != v.end(); ++it)
。至于x
,您可以在技术上使用int
来声明它,但是auto
限定符通常用于基于范围的for循环以进行自动类型推断。 - YuanLinTech使用迭代器有一些强大的理由,其中一些在此处提到:
即,如果您从std::vector切换到std::list或std::set,则无法使用数字索引访问包含的值。仍然可以使用迭代器。
如果您在循环中间修改了容器,则下次使用迭代器时它将抛出无效迭代器异常。
这里有一种更简单的方法来迭代并打印向量中的值。
for(int x: A) // for integer x in vector A
cout<< x <<" ";
不要忘记使用const修饰符的示例 - 循环能否修改元素。这里有很多例子,它们不能修改元素,并且应该使用const迭代器。在这里我们假设
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
}
使用STL,程序员使用迭代器来遍历容器,因为迭代器是一个抽象概念,在所有标准容器中都有实现。例如,std::list
根本没有operator []
。
std::vector<int>::size_type i = 0;
吗?或者可能是std::vector<int>::iterator it = vector.begin();
? - chrisstd::vector
时,什么是最好/最短的方法?”实际上,这里所问的问题是:“我为什么看不到这种用法在C ++中?它是不好的编程习惯吗?”也就是说,“为什么我总是看到在迭代std::vector
时使用迭代器的C++代码?” - Alok Save