为什么使用迭代器而不是数组索引?

采取以下两行代码:

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()是一个快速操作时,第一种forms才是有效的。 例如,这对于vector而言是正确的,但对于列表来说则是如此。 另外,你打算在循环体内做什么? 如果您计划访问中的元素

 T elem = some_vector[i]; 

那么你假设容器已经定义了operator[](std::size_t) 。 同样,这对于vector而言是正确的,但对于其他容器则不然。

迭代器的使用使您更接近容器独立性。 你并没有对随机访问能力或快速size()操作做出假设,只是容器具有迭代器function。

您可以使用标准algorithm进一步增强您的代码。 根据你要达到的目标,你可以select使用std::for_each()std::transform()等等。 通过使用标准algorithm而不是显式循环,您可以避免重新发明轮子。 你的代码可能更有效率(正确的algorithmselect),正确和可重用。

因为你没有把你的代码绑定到some_vector列表的特定实现。 如果你使用数组索引,它必须是某种forms的数组; 如果使用迭代器,则可以在任何列表实现上使用该代码。

这是现代C ++灌输过程的一部分。 迭代器是迭代大多数容器的唯一方法,所以即使使用vector也只是为了让自己进入正确的思维模式。 说真的,这是我做这件事的唯一原因 – 我不认为我用一种不同types的容器取代了一个载体。


哇,这个三星期后还是会下降 我想这不是一个小小的舌头。

我认为数组索引更具可读性。 它与其他语言中使用的语法以及用于旧式C数组的语法相匹配。 它也不那么冗长。 如果你的编译器是好的,那么效率就应该是一个清洗,而且几乎没有任何事情发生。

即使如此,我仍然发现自己经常使用vector迭代器。 我相信迭代器是一个重要的概念,所以我会尽我所能来推广它。

想象一下,some_vector是用链表实现的。 然后请求在第i个地方的项目需要我的操作来遍历节点列表。 现在,如果你使用迭代器,一般来说,它将尽最大努力尽可能高效(在链表的情况下,它将保持指向当前节点的指针并在每次迭代中推进它,只需要一个单一操作)。

所以它提供了两件事情:

  • 抽象的使用:你只是想迭代一些元素,你不关心如何去做
  • 性能

我将成为这里的恶魔主张,而不是推荐迭代器。 主要原因,是我从桌面应用程序开发到游戏开发的所有源代码,我也没有我需要使用迭代器。 所有的时间都没有被要求,其次是隐藏的假设和代码混乱和debugging噩梦你用迭代器使他们一个主要的例子,不要在任何需要速度的应用程序中使用它。

即使从维护的angular度来看,他们也是一团糟。 这不是因为他们,而是因为场景背后发生的所有走样。 我怎么知道你没有实现你自己的虚拟向量或者数组列表来完成与标准完全不同的事情。 我是否知道运行时现在是什么types? 你是否超载了一个运营商,我没有时间检查你的所有源代码。 地狱我什至知道你使用的是什么版本的STL?

迭代器的下一个问题是抽象漏洞,尽pipe有许多网站与他们详细讨论这个问题。

对不起,我还没有看到迭代器中的任何一点。 如果他们把这个列表或者vector从你身上抽象出来的话,那么事实上,你应该已经知道那个vector或者列表是如何处理的,如果你不这么做的话,那么你将来只需要做一些很好的debugging。

如果要在迭代器中添加/删除项目时,可能需要使用迭代器。

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

如果您使用的是索引,则必须在数组中向上/向下移动项目以处理插入和删除操作。

关注点分离

将迭代代码与循环的“核心”关注点分开是非常好的。 这几乎是一个devise决定。

事实上,通过索引来迭代,你可以实现容器。 询问容器的开始和结束迭代器,使循环代码与其他容器types一起使用。

另外,以std::for_each方式,你告诉集合该做什么,而不是问它关于它的内部的东西

0x标准将引入闭包,这将使这种方法更容易使用 – 看看Ruby的performance力[1..6].each { |i| print i; } [1..6].each { |i| print i; } [1..6].each { |i| print i; }

性能

但是,也许有一个值得注意的问题是,使用for_each方法可以让迭代平行化 – 英特尔线程块可以将代码块分配到系统中的处理器数量上!

注意:在发现了algorithms库之后,特别是在foreach ,我花了两三个月的时间写了一些可笑的小型“帮手”操作符,这会让你的开发人员疯狂起来。 在这段时间之后,我回到了一个务实的方式 – 小循环机构不应该再foreach了:)

迭代器必须阅读的参考书是“扩展STL” 。

GoF在迭代器模式结尾处有一个小小的段落,讲述了这个迭代的品牌; 它被称为“内部迭代器”。 看看这里也是

因为它更加面向对象。 如果你正在迭代一个你正在假设的索引:

a)这些对象是有序的
b)这些对象可以通过索引获得
c)索引增量将打击每个项目
d)该指数从零开始

用一个迭代器,你会说“给我所有东西,所以我可以用它”,而不知道底层的实现是什么。 (在Java中,有些集合不能通过索引访问)

另外,使用迭代器,不需要担心出数组的边界。

迭代器的另一个好处是它们更好地允许你expression(并强制)你的const-preference。 这个例子确保你不会改变循环中的向量:

 for(std::vector<Foo>::const_iterator pos=foos.begin(); pos != foos.end(); ++pos) { // Foo & foo = *pos; // this won't compile const Foo & foo = *pos; // this will compile } 

除了所有其他优秀的答案… int可能不够大,你的向量。 相反,如果您要使用索引,请使用容器的size_type

 for (std::vector<Foo>::size_type i = 0; i < myvector.size(); ++i) { Foo& this_foo = myvector[i]; // Do stuff with this_foo } 

我大概应该指出你也可以打电话

std::for_each(some_vector.begin(), some_vector.end(), &do_stuff);

STL迭代器大多是在那里,所以像sorting的STLalgorithm可以独立于容器。

如果你只是想循环一个向量中的所有条目,只需使用索引循环样式。

它更less打字,更容易parsing大多数人类。 如果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". } 

如果你认为在将来的某个时候你可能需要用一个列表来replace向量,那么使用迭代器会很好,而且对于STL怪胎来说,这看起来更时尚,但是我想不出任何其他的原因。

第二种forms代表你正在做的更准确。 在你的例子中,你不关心我的价值,真的 – 你想要的只是迭代器中的下一个元素。

在了解了这个答案的主题之后,我意识到这有点过分简化了。 这个循环之间的区别:

 for (some_iterator = some_vector.begin(); some_iterator != some_vector.end(); some_iterator++) { //do stuff } 

而这个循环:

 for (int i = 0; i < some_vector.size(); i++) { //do stuff } 

是相当小的。 事实上,这样做循环的语法似乎越来越多:

 while (it != end){ //do stuff ++it; } 

迭代器可以解开一些相当强大的声明性特性,当与STLalgorithm库结合使用时,可以做一些超出数组索引pipe理范围的非常酷的事情。

索引需要额外的操作。 例如,对于vectorv,编译器将v [i]转换为&v + sizeof(int)* i。

在迭代期间,您不需要知道要处理的项目的数量。 你只需要这个项目,迭代器就可以做很好的事情。

已经有几个好点了。 我还有几点意见:

  1. 假设我们正在讨论C ++标准库,“vector”意味着一个随机访问容器,它具有C-array(随机访问,连续性内存布局等)的保证。 如果你说'some_container',上面的答案中的许多将会更准确(容器独立性等)。

  2. 为了消除对编译器优化的依赖,可以将some_vector.size()移出索引代码中的循环,如下所示:

      const size_t numElems = some_vector.size();
     for(size_t i = 0; i 
  3. 总是预增加迭代器,并将后增量视为特例。

for(some_iterator = some_vector.begin(); some_iterator!= some_vector.end(); ++ some_iterator){// do stuff}

所以假设和可索引的std::vector<>像容器,没有什么好的理由比其他的顺序通过容器。 如果你不得不频繁地引用更老或更新的索引索引,那么索引版本更适合。

一般来说,使用迭代器是首选,因为algorithm使用它们,行为可以通过改变迭代器的types来控制(和隐式logging)。 数组位置可以用来代替迭代器,但是语法上的差异将会突出。

我不使用迭代器出于同样的原因,我不喜欢foreach语句。 当有多个内部循环时,很难跟踪全局/成员variables,而不必记住所有本地值和迭代器名称。 我觉得有用的是在不同场合使用两套指数:

 for(int i=0;i<anims.size();i++) for(int j=0;j<bones.size();j++) { int animIndex = i; int boneIndex = j; // in relatively short code I use indices i and j ... animation_matrices[i][j] ... // in long and complicated code I use indices animIndex and boneIndex ... animation_matrices[animIndex][boneIndex] ... } 

我甚至不想把“animation_matrices [i]”这样的东西缩写成一些随机的“anim_matrix” – 命名迭代器,因为那样你就不能清楚地看到这个值是从哪个数组开始的。

甚至比“告诉CPU做什么”(必要的)更好的是“告诉图书馆你想要什么”(function)。

所以不要使用循环,而应该学习stl中的algorithm。

为容器独立

我总是使用数组索引,因为我的许多应用程序需要像“显示缩略图”。 所以我写了这样的东西:

 some_vector[0].left=0; some_vector[0].top =0;<br> for (int i = 1; i < some_vector.size(); i++) { some_vector[i].left = some_vector[i-1].width + some_vector[i-1].left; if(i % 6 ==0) { some_vector[i].top = some_vector[i].top.height + some_vector[i].top; some_vector[i].left = 0; } } 

这两个实现都是正确的,但我更喜欢'for'循环。 因为我们决定使用Vector而不是其他容器,所以使用索引是最好的select。 对vector使用迭代器将失去使对象处于连续内存块中的好处,这有助于简化访问。

  • 如果你喜欢靠近金属/不信任他们的实现细节, 不要使用迭代器。
  • 如果您在开发过程中定期为其他types创build另一个集合types,请使用迭代器。
  • 如果你很难记住如何迭代不同types的集合(也许你有几种不同types的外部源),可以使用迭代器统一你遍历元素的方法。 这适用于使用数组列表切换链接列表。

真的,这就是它的全部。 并不是说你要平均得到更简洁的方法,如果简洁真的是你的目标,你总是可以回到macros观上。

没有人提到索引的一个优点是,当你追加到像std::vector这样的连续容器时,它们不会变得无效,所以你可以在迭代期间向容器添加项目。

这也可以用迭代器,但是你必须调用reserve() ,因此需要知道你会追加多less个项目。