题
采取以下两个行代码:
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个地方需要我的操作要做,以穿越节点列表。现在,如果您使用的迭代,一般来说,它将尽其最大努力尽可能有效地(在这种情况下的一个链表,它将保持一个指向现节点,并推进它在每次迭代,需要的只是一个单一的操作)。
因此,它提供了两件事:
- 抽象的使用:你只是想要迭代的一些元素,你不关心如何做到这一点
- 性能
我将成为这里的恶魔倡导者,不推荐使用迭代器。主要原因是,我从桌面应用程序开发到游戏开发所使用的所有源代码都有我也不需要使用迭代器。它们一直没有被要求,其次是使用迭代器得到的隐藏的假设和代码混乱以及调试噩梦使它们成为不在任何需要速度的应用程序中使用它的主要例子。
即使从维护的角度来看,他们也是一团糟。它不是因为它们,而是因为场景背后发生的所有混叠。我怎么知道你没有实现自己的虚拟向量或数组列表,它们与标准完全不同。我是否知道运行时当前的类型?您是否超载了操作员我没有时间检查所有源代码。地狱,我甚至知道你使用的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
方法产生了一个让迭代并行化的机会 - intel threading blocks 可以将代码块分配给系统中的处理器数量!
注意:在发现了algorithms
库,特别是foreach
之后,我经历了两三个月的小编写'帮助'操作符结构,这会让你的开发人员疯狂。在这段时间之后,我回到了一个务实的方法 - 小循环体不值得<=>不再:)
关于迭代器的必读参考书是 <!>“扩展STL <!>”
GoF在Iterator模式的末尾有一个小小的段落,它讨论了这个迭代的品牌;它被称为“内部迭代器”。看看这里。
因为它更面向对象。如果你正在迭代你假设的索引:
a)那些对象是有序的
b)这些对象可以通过索引获得
c)索引增量将击中每个项目
d)该指数从零开始
使用迭代器,你说<!>“;给我一切,这样我就可以使用<!>”;不知道底层实现是什么。 (在Java中,有些集合无法通过索引访问)
此外,使用迭代器,无需担心超出数组的范围。
迭代器的另一个好处是它们更好地允许你表达(并强制执行)你的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迭代器主要存在,因此像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怪胎但我想不出任何其他原因。
第二种形式代表你正在做的更准确。在你的例子中,你不关心i的值,真的 - 你想要的只是迭代器中的下一个元素。
在对这个答案的主题进行了一些学习之后,我意识到这有点过于简单了。这个循环之间的区别:
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;
}
迭代器确实解锁了一些相当强大的声明性功能,当与STL算法库结合使用时,你可以做一些超出数组索引administrivia范围的非常酷的东西。
索引需要额外的mul
操作。例如,对于vector<int> v
,编译器将v[i]
转换为&v + sizeof(int) * i
。
在迭代过程中,您不需要知道要处理的项目数。你只需要这个项目,迭代器就可以做得非常好。
没有人提到索引的一个优点是当你追加到像std::vector
这样的连续容器时它们不会变得无效,所以你可以在迭代期间向容器添加项目。
迭代器也可以这样做,但你必须调用reserve()
,因此需要知道你要添加多少项。
已有好几点。我还有一些意见:
-
假设我们正在谈论C ++标准库,<!> quot; vector <!> quot;意味着一个随机访问容器,它具有C阵列的保证(随机访问,连续内存布局等)。如果您说'some_container',上面的许多答案会更准确(容器独立等)。
-
要消除对编译器优化的任何依赖,可以将some_vector.size()移出索引代码中的循环,如下所示:
const size_t numElems = some_vector.size(); for (size_t i = 0; i
-
始终预先增加迭代器并将后增量视为例外情况。
醇>
for(some_iterator = some_vector.begin(); some_iterator!= some_vector.end(); ++ some_iterator){// do stuff}
因此假设和可索引的std::vector<>
像容器一样,没有充分的理由偏爱其他人,顺序通过容器。如果您必须经常引用较旧或较新的elemnent索引,则索引版本更为适用。
通常,首选使用迭代器是因为算法使用它们,并且可以通过更改迭代器的类型来控制(并隐式记录)行为。可以使用数组位置代替迭代器,但语法差异将会突然出现。
我不使用迭代器的原因与我不喜欢foreach语句的原因相同。当有多个内部循环时,很难跟踪全局/成员变量,而不必记住所有本地值和迭代器名称。我觉得有用的是为不同的场合使用两组索引:
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] ...
}
我甚至不想缩写<!> quot; animation_matrices [i] <!> quot;例如,一些随机的<!>“anim_matrix <!>” - -named-iterator,因为那时你无法清楚地看到这个值来自哪个数组。
- 如果你喜欢正在附近的金属/不信任他们的执行情况的详细信息, 不要用 迭代器。
- 如果你经常开了一个收集类型的另一个在发展, 使用 迭代器。
- 如果你觉得很难记得怎么迭代表不同种类的集(也许你有几种类型的从几个不同的外部来源在使用中), 使用 迭代统一的手段,通过它,你走路过来的要素。这适用说交换链表与一系列的清单。
真的,那就是所有的。这并不是因为如果你要获得更多的简洁无论哪种方式上的平均水平,并简洁起见,如果真的是你的目标是,你总是可以依靠宏。
甚至比<!>更好;告诉CPU要做什么<!> (命令式)是<!>“;告诉图书馆你想要什么<!>”; (功能性)。
因此,您应该学习stl。
中存在的算法,而不是使用循环对于容器独立性
我总是使用数组索引,因为我的许多应用程序需要类似<!>“显示缩略图<!>”的内容。所以我写了这样的话:
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而不是任何其他容器,因此使用索引将是最佳选择。使用带有向量的迭代器将失去将对象放在连续内存块中的好处,这有助于简化对象的访问。