请使用以下两行代码:
for (int i = 0; i < some_vector.size(); i++) { //do stuff }
还有这个:
for (some_iterator = some_vector.begin(); some_iterator != some_vector.end(); some_iterator++) { //do stuff }
我被告知第二种方式是首选.这究竟是为什么?
仅当vector.size()是快速操作时,第一种形式才有效.例如,对于向量而言,这是正确的,但对于列表则不然.另外,你打算在循环体内做些什么?如果您计划访问元素,请参阅
T elem = some_vector[i];
然后你假设容器已经operator[](std::size_t)
定义了.同样,这适用于矢量,但不适用于其他容器.
迭代器的使用使您更接近容器独立性.您没有假设随机访问能力或快速size()
操作,只是容器具有迭代器功能.
您可以使用标准算法进一步增强代码.根据您尝试实现的目标,您可以选择使用std::for_each()
,std::transform()
等等.通过使用标准算法而不是显式循环,您可以避免重新发明轮子.您的代码可能更有效(假设选择了正确的算法),正确且可重用.
因为您没有将代码绑定到some_vector列表的特定实现.如果你使用数组索引,它必须是某种形式的数组; 如果使用迭代器,则可以在任何列表实现上使用该代码.
它是现代C++灌输过程的一部分.迭代器是迭代大多数容器的唯一方法,因此您甚至可以使用向量来使自己进入正确的思维模式.说真的,这是我这样做的唯一原因 - 我不认为我曾经用一种不同类型的容器替换了一个载体.
我认为数组索引更具可读性.它匹配其他语言中使用的语法,以及用于老式C数组的语法.它也不那么冗长.如果您的编译器有任何好处,效率应该是一个很好的,并且几乎没有任何情况下它很重要.
即便如此,我仍然发现自己经常使用迭代器和向量.我相信迭代器是一个重要的概念,所以我会随时提升它.
想象一下some_vector是用链表实现的.然后请求第i个位置的项目需要进行i操作以遍历节点列表.现在,如果你使用迭代器,一般来说,它将尽最大努力尽可能高效(在链表的情况下,它将维护一个指向当前节点的指针并在每次迭代中推进它,只需要一个单一操作).
所以它提供了两件事:
抽象使用:你只想迭代一些元素,你不关心如何去做
性能
我将成为这里的恶魔倡导者,而不是推荐迭代器.主要原因是,我从桌面应用程序开发到游戏开发所使用的所有源代码都有我也不需要使用迭代器.它们一直没有被要求,其次是使用迭代器得到的隐藏的假设和代码混乱以及调试噩梦使它们成为不在任何需要速度的应用程序中使用它的主要例子.
即使从维护的角度来看,他们也是一团糟.它不是因为它们,而是因为场景背后发生的所有混叠.我怎么知道你没有实现自己的虚拟向量或数组列表,它们与标准完全不同.我是否知道运行时当前的类型?您是否超载了操作员我没有时间检查所有源代码.地狱,我甚至知道你使用的STL版本是什么?
迭代器带来的下一个问题是漏洞抽象,尽管有很多网站都会详细讨论这个问题.
对不起,我还没有看到迭代器中的任何一点.如果他们将列表或向量从你身上抽象出来,实际上你应该知道你处理的是什么向量或列表,如果你没有,那么你将来会为自己设置一些很棒的调试会话.
如果要在迭代时向向量添加/删除项目,可能需要使用迭代器.
some_iterator = some_vector.begin(); while (some_iterator != some_vector.end()) { if (/* some condition */) { some_iterator = some_vector.erase(some_iterator); // some_iterator now positioned at the element after the deleted element } else { if (/* some other condition */) { some_iterator = some_vector.insert(some_iterator, some_new_value); // some_iterator now positioned at new element } ++some_iterator; } }
如果您使用索引,则必须在数组中向上/向下移动项目以处理插入和删除.
将迭代代码与循环的"核心"关注分开是非常好的.这几乎是一个设计决定.
实际上,通过索引迭代会将您与容器的实现联系起来.向容器请求开始和结束迭代器,启用循环代码以与其他容器类型一起使用.
此外,在这种std::for_each
方式中,你告诉集合做什么,而不是询问它的内部
0x标准将引入闭包,这将使这种方法更容易使用 - 看看例如Ruby的表达能力[1..6].each { |i| print i; }
......
但也许一个受到很多监督的问题是,使用这种for_each
方法产生了一个让迭代并行化的机会 - 英特尔线程模块可以将代码块分配给系统中的多个处理器!
注意:在发现algorithms
库之后,特别是foreach
,我经历了两三个月的小编写"帮助"操作符结构,这会让你的开发人员疯狂.在这段时间之后,我回到了一个务实的方法 - 小循环体不值得foreach
更多:)
关于迭代器的必读参考是"扩展STL"一书.
GoF在Iterator模式的末尾有一个小小的段落,它讨论了这个迭代的品牌; 它被称为"内部迭代器".看看这里也是.
因为它更面向对象.如果您正在使用您正在假设的索引进行迭代:
a)那些对象是有序的
b)这些对象可以通过索引获得
c)索引增量将命中每个项目
d)该索引从零开始
使用迭代器,你会说"给我一切,所以我可以使用它",而不知道底层实现是什么.(在Java中,有些集合无法通过索引访问)
此外,使用迭代器,无需担心超出数组的范围.
除了所有其他优秀答案之外...... int
对于你的矢量来说可能不够大.相反,如果要使用索引,请使用size_type
容器:
for (std::vector::size_type i = 0; i < myvector.size(); ++i) { Foo& this_foo = myvector[i]; // Do stuff with this_foo }
迭代器的另一个好处是它们更好地允许你表达(并强制执行)你的const-preference.此示例确保您不会在循环中更改向量:
for(std::vector::const_iterator pos=foos.begin(); pos != foos.end(); ++pos) { // Foo & foo = *pos; // this won't compile const Foo & foo = *pos; // this will compile }
我可能应该指出你也可以打电话
std::for_each(some_vector.begin(), some_vector.end(), &do_stuff);
STL迭代器主要存在,因此像sort这样的STL算法可以与容器无关.
如果您只想循环遍历向量中的所有条目,只需使用索引循环样式.
对于大多数人来说,打字更少,更容易解析.如果C++有一个简单的foreach循环而不会过度使用模板魔法会很好.
for( size_t i = 0; i < some_vector.size(); ++i ) { T& rT = some_vector[i]; // now do something with rT } '
我认为它不会对矢量产生太大影响.我更喜欢自己使用索引,因为我认为它更具可读性,你可以随机访问,例如跳转6项或者如果需要可以跳回去.
我也喜欢像这样在循环中引用项目,所以这个地方周围没有很多方括号:
for(size_t i = 0; i < myvector.size(); i++) { MyClass &item = myvector[i]; // Do stuff to "item". }
使用迭代器可能会很好,如果你认为你可能需要在将来的某个时候用一个列表替换向量,它对于STL怪胎看起来也更时尚,但我想不出任何其他原因.