以下面两行代码为例:
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
}
有人告诉我第二种方法更可取。为什么会这样呢?
以下面两行代码为例:
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
}
有人告诉我第二种方法更可取。为什么会这样呢?
当前回答
分离关注点
将迭代代码从循环的“核心”关注点中分离出来是非常好的。这几乎是一个设计决策。
实际上,通过索引迭代将您与容器的实现联系在一起。向容器请求开始和结束迭代器,使循环代码可用于其他容器类型。
同样,在std::for_each方法中,你告诉集合要做什么,而不是询问它的内部信息
0x标准将引入闭包,这将使这种方法更容易使用-看看例如Ruby的[1..6]的表达能力。每个{|i| print i;}……
性能
但是可能有一个被监督的问题是,使用for_each方法产生了一个并行迭代的机会——intel线程块可以将代码块分布在系统中的处理器数量上!
注意:在发现算法库,特别是foreach之后,我花了两三个月的时间写了非常小的“helper”操作符结构,这会让其他开发人员发疯。在这之后,我回到了一个实用的方法-小循环体不应该再有foreach了:)
关于迭代器的必读参考书是《扩展STL》。
GoF在迭代器模式的末尾有一小段话,讲的是这种迭代;它被称为“内部迭代器”。这里也看看。
其他回答
如果要在迭代vector时向其添加/删除项,则可能需要使用迭代器。
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;
}
}
如果使用索引,则必须在数组中上下移动项以处理插入和删除。
因为您没有将代码绑定到some_vector列表的特定实现。如果你使用数组下标,它必须是某种形式的数组;如果使用迭代器,则可以在任何列表实现上使用该代码。
只有当vector.size()是一个快速操作时,第一种形式才有效。这对于向量是正确的,但是对于列表就不是这样了。另外,您计划在循环体中做什么?如果您计划访问元素,如
T elem = some_vector[i];
然后假设容器定义了operator[](std::size_t)。同样,这适用于vector容器,但不适用于其他容器。
迭代器的使用使您更接近容器独立性。您没有假设随机访问能力或快速size()操作,只是假设容器具有迭代器功能。
您可以通过使用标准算法进一步增强代码。根据你想要实现的目标,你可以选择使用std::for_each(), std::transform()等等。通过使用标准算法而不是显式循环,您可以避免重新发明轮子。您的代码可能更高效(如果选择了正确的算法)、正确和可重用。
我在这里是魔鬼的倡导者,不推荐使用迭代器。主要原因是,我从桌面应用程序开发到游戏开发的所有源代码都没有我也不需要使用迭代器。一直以来,迭代器都不是必需的,其次,迭代器所带来的隐藏假设、代码混乱和调试噩梦,使其成为任何要求速度的应用程序都不要使用的典型例子。
即使从维护的角度来看,它们也是一团糟。这并不是因为它们,而是因为所有发生在幕后的混叠。我怎么知道你没有实现你自己的虚拟向量或数组列表,做一些完全不同的标准。我知道什么类型的当前,现在在运行时?你是否重载了运算符我没有时间检查你所有的源代码。我甚至不知道你使用的STL是什么版本?
迭代器的下一个问题是抽象漏洞,尽管有许多网站对此进行了详细讨论。
对不起,我没有,现在也没有看到迭代器有任何意义。如果他们抽象了列表或向量,而实际上你应该已经知道你要处理什么向量或列表,如果你不知道,那么你只会为将来的一些伟大的调试会话做好准备。
还没有人提到索引的一个优点是,当您向std::vector这样的连续容器添加索引时,它们不会失效,因此您可以在迭代期间向容器添加项。
这也可以用迭代器实现,但必须调用reserve(),因此需要知道要追加多少项。