C++ 迭代器循环与索引循环
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14373934/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Iterator Loop vs index loop
提问by CodingMadeEasy
Possible Duplicate:
Why use iterators instead of array indices?
可能的重复:
为什么使用迭代器而不是数组索引?
I'm reviewing my knowledge on C++ and I've stumbled upon iterators. One thing I want to know is what makes them so special and I want to know why this:
我正在复习我对 C++ 的了解,并且偶然发现了迭代器。我想知道的一件事是是什么让它们如此特别,我想知道为什么会这样:
using namespace std;
vector<int> myIntVector;
vector<int>::iterator myIntVectorIterator;
// Add some elements to myIntVector
myIntVector.push_back(1);
myIntVector.push_back(4);
myIntVector.push_back(8);
for(myIntVectorIterator = myIntVector.begin();
myIntVectorIterator != myIntVector.end();
myIntVectorIterator++)
{
cout<<*myIntVectorIterator<<" ";
//Should output 1 4 8
}
is better than this:
比这更好:
using namespace std;
vector<int> myIntVector;
// Add some elements to myIntVector
myIntVector.push_back(1);
myIntVector.push_back(4);
myIntVector.push_back(8);
for(int y=0; y<myIntVector.size(); y++)
{
cout<<myIntVector[y]<<" ";
//Should output 1 4 8
}
And yes I know that I shouldn't be using the std namespace. I just took this example off of the cprogramming website. So can you please tell me why the latter is worse? What's the big difference?
是的,我知道我不应该使用 std 命名空间。我刚刚从 cprogramming 网站上拿走了这个例子。那么你能告诉我为什么后者更糟吗?有什么大的区别?
回答by TemplateRex
The special thing about iterators is that they provide the glue between algorithms and containers. For generic code, the recommendation would be to use a combination of STL algorithms (e.g. find
, sort
, remove
, copy
) etc. that carries out the computation that you have in mind on your data structure (vector
, list
, map
etc.), and to supply that algorithm with iterators into your container.
迭代器的特殊之处在于它们提供了算法和容器之间的粘合剂。对于通用代码的建议是使用STL算法的组合(例如find
,sort
,remove
,copy
)等,其执行的计算,你心里有你的数据结构(vector
,list
,map
等),并与提供该算法迭代器进入你的容器。
Your particular example could be written as a combination of the for_each
algorithm and the vector
container (see option 3) below), but it's only one out of four distinct ways to iterate over a std::vector:
您的特定示例可以编写为for_each
算法和vector
容器的组合(请参阅下面的选项 3)),但这只是迭代 std::vector 的四种不同方式中的一种:
1) index-based iteration
1)基于索引的迭代
for (std::size_t i = 0; i != v.size(); ++i) {
// access element as v[i]
// any code including continue, break, return
}
Advantages: familiar to anyone familiar with C-style code, can loop using different strides (e.g. i += 2
).
优点:熟悉 C 风格代码的人都熟悉,可以使用不同的步幅(例如i += 2
)进行循环。
Disadvantages: only for sequential random access containers (vector
, array
, deque
), doesn't work for list
, forward_list
or the associative containers. Also the loop control is a little verbose (init, check, increment). People need to be aware of the 0-based indexing in C++.
缺点:仅适用于顺序随机访问容器(vector
、array
、deque
),不适用于list
、forward_list
或关联容器。循环控制也有点冗长(初始化、检查、增量)。人们需要了解 C++ 中基于 0 的索引。
2) iterator-based iteration
2)基于迭代器的迭代
for (auto it = v.begin(); it != v.end(); ++it) {
// if the current index is needed:
auto i = std::distance(v.begin(), it);
// access element as *it
// any code including continue, break, return
}
Advantages: more generic, works for all containers (even the new unordered associative containers, can also use different strides (e.g. std::advance(it, 2)
);
优点:更通用,适用于所有容器(即使是新的无序关联容器,也可以使用不同的步幅(例如std::advance(it, 2)
);
Disadvantages: need extra work to get the index of the current element (could be O(N) for list or forward_list). Again, the loop control is a little verbose (init, check, increment).
缺点:需要额外的工作来获取当前元素的索引(对于 list 或 forward_list 可能是 O(N))。同样,循环控制有点冗长(初始化、检查、增量)。
3) STL for_each algorithm + lambda
3) STL for_each 算法 + lambda
std::for_each(v.begin(), v.end(), [](T const& elem) {
// if the current index is needed:
auto i = &elem - &v[0];
// cannot continue, break or return out of the loop
});
Advantages: same as 2) plus small reduction in loop control (no check and increment), this can greatly reduce your bug rate (wrong init, check or increment, off-by-one errors).
优点:与 2) 相同,加上循环控制的小幅减少(无检查和增量),这可以大大降低您的错误率(错误的初始化、检查或增量、逐一错误)。
Disadvantages: same as explicit iterator-loop plus restricted possibilities for flow control in the loop (cannot use continue, break or return) and no option for different strides (unless you use an iterator adapter that overloads operator++
).
缺点:与显式迭代器循环相同,加上循环中流量控制的受限可能性(不能使用 continue、break 或 return)并且没有不同步幅的选项(除非您使用重载的迭代器适配器operator++
)。
4) range-for loop
4) range-for 循环
for (auto& elem: v) {
// if the current index is needed:
auto i = &elem - &v[0];
// any code including continue, break, return
}
Advantages: very compact loop control, direct access to the current element.
优点:非常紧凑的回路控制,直接访问电流元件。
Disadvantages: extra statement to get the index. Cannot use different strides.
缺点:需要额外的语句来获取索引。不能使用不同的步幅。
What to use?
用什么?
For your particular example of iterating over std::vector
: if you really need the index (e.g. access the previous or next element, printing/logging the index inside the loop etc.) or you need a stride different than 1, then I would go for the explicitly indexed-loop, otherwise I'd go for the range-for loop.
对于您迭代的特定示例std::vector
:如果您确实需要索引(例如访问上一个或下一个元素,在循环内打印/记录索引等)或者您需要一个不同于 1 的步幅,那么我会明确地选择索引循环,否则我会选择 range-for 循环。
For generic algorithms on generic containers I'd go for the explicit iterator loop unless the code contained no flow control inside the loop and needed stride 1, in which case I'd go for the STL for_each
+ a lambda.
对于通用容器上的通用算法,我会使用显式迭代器循环,除非代码在循环内不包含流控制并且需要步长 1,在这种情况下,我会使用 STL for_each
+ lambda。
回答by Alok Save
Iterators make your code more generic.
Every standard library container provides an iterator hence if you change your container class in future the loop wont be affected.
迭代器使您的代码更通用。
每个标准库容器都提供一个迭代器,因此如果您将来更改容器类,循环不会受到影响。
回答by 6502
With a vector iterators do no offer any real advantage. The syntax is uglier, longer to type and harder to read.
使用向量迭代器并没有提供任何真正的优势。语法更丑,打字时间更长,更难阅读。
Iterating over a vector using iterators is not faster and is not safer (actually if the vector is possibly resized during the iteration using iterators will put you in big troubles).
使用迭代器迭代一个向量并不快,也不安全(实际上,如果在使用迭代器的迭代过程中可能调整向量的大小,会给你带来很大的麻烦)。
The idea of having a generic loop that works when you will change later the container type is also mostly nonsense in real cases. Unfortunately the dark side of a strictly typed language without serious typing inference (a bit better now with C++11, however) is that you need to say what is the type of everything at each step. If you change your mind later you will still need to go around and change everything. Moreover different containers have very different trade-offs and changing container type is not something that happens that often.
当您稍后更改容器类型时,拥有一个通用循环的想法在实际情况下也大多是无稽之谈。不幸的是,没有严格的类型推断的严格类型语言的阴暗面(但是现在使用 C++11 好一点)是您需要在每一步都说明所有内容的类型。如果您稍后改变主意,您仍然需要四处走动并改变一切。此外,不同的容器有非常不同的权衡,改变容器类型并不是经常发生的事情。
The only case in which iteration should be kept if possible generic is when writing template code, but that (I hope for you) is not the most frequent case.
如果可能的话,唯一应该保持迭代通用的情况是在编写模板代码时,但这(我希望你)不是最常见的情况。
The only problem present in your explicit index loop is that size
returns an unsigned value (a design bug of C++) and comparison between signed and unsigned is dangerous and surprising, so better avoided. If you use a decent compiler with warnings enabled there should be a diagnostic on that.
显式索引循环中存在的唯一问题是size
返回无符号值(C++ 的设计错误),并且有符号和无符号之间的比较是危险且令人惊讶的,因此最好避免。如果您使用启用了警告的体面编译器,那么应该有一个诊断。
Note that the solution is not to use an unsiged as the index, because arithmetic between unsigned values is also apparently illogical (it's modulo arithmetic, and x-1
may be bigger than x
). You instead should cast the size to an integer before using it.
It maymake some sense to use unsigned sizes and indexes (paying a LOT of attention to every expression you write) only if you're working on a 16 bit C++ implementation (16 bit was the reason for having unsigned values in sizes).
请注意,解决方案不是使用 unsiged 作为索引,因为无符号值之间的算术显然也是不合逻辑的(它是模算术,并且x-1
可能大于x
)。相反,您应该在使用它之前将大小转换为整数。它可以做一些有意义的使用无符号的大小和索引(支付的关注很多你写的每一个表情),只有当你在一个16位的C ++实现的工作(16位是在大小为无符号值的原因)。
As a typical mistake that unsigned size may introduce consider:
作为无符号大小可能引入的典型错误,请考虑:
void drawPolyline(const std::vector<P2d>& points)
{
for (int i=0; i<points.size()-1; i++)
drawLine(points[i], points[i+1]);
}
Here the bug is present because if you pass an empty points
vector the value points.size()-1
will be a huge positive number, making you looping into a segfault.
A working solution could be
这里存在错误,因为如果您传递一个空points
向量,该值points.size()-1
将是一个巨大的正数,使您循环进入段错误。一个可行的解决方案可能是
for (int i=1; i<points.size(); i++)
drawLine(points[i - 1], points[i]);
but I personally prefer to always remove unsinged
-ness with int(v.size())
.
但我个人更喜欢总是unsinged
使用int(v.size())
.
PS: If you really don't want to think by to yourself to the implications and simply want an expert to tell you then consider that a quite a few world recognized C++ experts agree and expressed opinions on that unsigned values are a bad idea except for bit manipulations.
PS:如果你真的不想自己思考其中的含义,而只是想让专家告诉你,那么请考虑很多世界公认的 C++ 专家同意并表达了对无符号值的看法,除了位操作。
Discovering the ugliness of using iterators in the case of iterating up to second-last is left as an exercise for the reader.
在迭代到倒数第二个的情况下发现使用迭代器的丑陋之处留给读者作为练习。
回答by billz
Iterators are first choice over operator[]
. C++11 provides std::begin()
, std::end()
functions.
迭代器是首选operator[]
。C++11 提供了std::begin()
,std::end()
函数。
As your code uses just std::vector
, I can't say there is much difference in both codes, however, operator []
may not operate as you intend to. For example if you use map, operator[]
will insert an element if not found.
由于您的代码仅使用std::vector
,我不能说这两种代码有很大差异,但是,operator []
可能不会按您的意图运行。例如,如果您使用地图,operator[]
如果未找到,将插入一个元素。
Also, by using iterator
your code becomes more portable between containers. You can switch containers from std::vector
to std::list
or other container freely without changing much if you use iterator such rule doesn't apply to operator[]
.
此外,通过使用iterator
您的代码在容器之间变得更加可移植。您可以从容器切换std::vector
到std::list
或其他容器自由地在不改变太多,如果你使用迭代器这样的规则并不适用于operator[]
。
回答by Mark Garcia
It always depends on what you need.
这总是取决于你需要什么。
You should use operator[]
when you needdirect access to elements in the vector (when you need to index a specific element in the vector). There is nothing wrong in using it over iterators. However, you must decide for yourself which (operator[]
or iterators) suits best your needs.
operator[]
当您需要直接访问向量中的元素时(当您需要索引向量中的特定元素时),您应该使用。在迭代器上使用它没有错。但是,您必须自己决定哪个(operator[]
或迭代器)最适合您的需求。
Using iterators would enable you to switch to other container types without much change in your code. In other words, using iterators would make your code more generic, and does not depend on a particular type of container.
使用迭代器将使您能够切换到其他容器类型,而无需对代码进行太多更改。换句话说,使用迭代器将使您的代码更通用,并且不依赖于特定类型的容器。
回答by utnapistim
By writing your client code in terms of iterators you abstract away the container completely.
通过根据迭代器编写客户端代码,您可以完全抽象出容器。
Consider this code:
考虑这个代码:
class ExpressionParser // some generic arbitrary expression parser
{
public:
template<typename It>
void parse(It begin, const It end)
{
using namespace std;
using namespace std::placeholders;
for_each(begin, end,
bind(&ExpressionParser::process_next, this, _1);
}
// process next char in a stream (defined elsewhere)
void process_next(char c);
};
client code:
客户端代码:
ExpressionParser p;
std::string expression("SUM(A) FOR A in [1, 2, 3, 4]");
p.parse(expression.begin(), expression.end());
std::istringstream file("expression.txt");
p.parse(std::istringstream<char>(file), std::istringstream<char>());
char expr[] = "[12a^2 + 13a - 5] with a=108";
p.parse(std::begin(expr), std::end(expr));
Edit: Consider your original code example, implemented with :
编辑:考虑您的原始代码示例,使用以下代码实现:
using namespace std;
vector<int> myIntVector;
// Add some elements to myIntVector
myIntVector.push_back(1);
myIntVector.push_back(4);
myIntVector.push_back(8);
copy(myIntVector.begin(), myIntVector.end(),
std::ostream_iterator<int>(cout, " "));
回答by Caesar
The nice thing about iterator is that later on if you wanted to switch your vector to a another STD container. Then the forloop will still work.
迭代器的好处在于,稍后如果您想将向量切换到另一个 STD 容器。然后 forloop 仍然可以工作。
回答by Nicolas Brown
its a matter of speed. using the iterator accesses the elements faster. a similar question was answered here:
它的速度问题。使用迭代器可以更快地访问元素。这里回答了一个类似的问题:
What's faster, iterating an STL vector with vector::iterator or with at()?
使用 vector::iterator 或 at() 迭代 STL 向量,哪个更快?
Edit: speed of access varies with each cpu and compiler
编辑:访问速度因每个 CPU 和编译器而异