每当有人开始使用STL并且他们有一个向量时,您通常会看到:
vector<int> vec ; //... code ... for( vector<int>::iterator iter = vec.begin() ; iter != vec.end() ; ++iter ) { // do stuff }
我发现整个vector<int>::iterator
语法都在恶化。我知道你可以typedef vector<int>::iterator VecIterInt
, 稍微好一些......
但问题是,好的OL有什么问题:
for( int i = 0 ; i < vec.size() ; i++ ) { // code }
答案 0 :(得分:11)
当您使用索引执行对容器(std::vector
或其他任何内容)的顺序访问时,您将随机访问要求强加于基础数据结构,实际上你的算法中不需要这种访问。与顺序访问的明显较弱的要求相比,随机访问要求是非常强烈的要求。没有充分理由强加更强的要求是一个重大的设计错误。
所以你的问题的正确答案是:尽可能使用顺序(迭代器)访问,只有在你必须时才使用随机(索引)访问。尽可能避免索引访问。
如果您的算法严格依赖于随机可访问的容器,它将成为算法的外部要求。在这种情况下,您可以使用索引访问而无需任何保留。但是,如果只能使用迭代器来实现相同的算法,那么最好只坚持使用迭代器,即完全依赖顺序访问。
当然,上述规则虽然属实,但在某些程度上只在代码泛型中才有意义。如果代码的其他部分是特定的,那么您确定您使用的数据结构是std::vector
且始终是std::vector
,那么访问方法不再重要。用你喜欢的任何东西。但是,在顺序访问完全足够的情况下,我仍然会避免索引访问。
答案 1 :(得分:7)
当谈到std::vector
时,我认为在循环中使用下标运算符就好了,在性能方面可能大致相同。使用迭代器的优点在于您希望将向量与&lt; algorithms&gt;中的其他std
函数一起使用。
答案 2 :(得分:2)
我认为我的论点不是很强,但我几乎总是使用迭代器版本。
typedef std::vector<int> MyIndexes; // or whatever
MyIndexes indexes;
for (Something::iterator iter = indexes.begin(); iter != indexes.end(); ++iter);
现在,如果我必须将矢量更改为列表或类似的东西,我只需要更改我的typedef。这在几个场合很有用。 auto关键字会让这更好,但我不能等待C ++ 0x for loop:)
答案 3 :(得分:1)
我通常对矢量使用for循环 - 对我而言,这是处理这些事情的规范方法。并且不要让任何人告诉你迭代器更快 - 请参阅What's faster, iterating an STL vector with vector::iterator or with at()?。
另外,像foreach这样的迭代器和构造的极端支持者倾向于忽略的一件事是,使用for循环,你实际上有一个整数索引,你可以用除了访问集合元素以外的东西来做事情,这可能非常有用在各种情况下。
但是,如果我使用STL算法,或者迭代其他容器类型,例如std :: map,我当然会使用迭代器。
答案 4 :(得分:1)
作为一般规则,不限于std :: vector,迭代器可以比索引更有效,因为它们可以访问集合的内部并知道迭代的状态。
在std :: vector的情况下,循环中的迭代器将在使用优化编译时减少为指针算术。聪明的编译器可能能够对计数器循环执行相同的操作,并且性能不应该有任何差别,但一般情况下(对于更复杂的集合),您应该假设迭代器将为您提供最快的代码。
答案 5 :(得分:1)
使用C ++ 0x你不会有这种困境。您将使用新的for
which is actually foreach:)
答案 6 :(得分:1)
我建议使用迭代器只是因为它们更通用。如果在开发周期的后期,您决定使用std :: list&lt;&gt;会比std :: vector&lt;&gt;更好(也许你发现你有一个性能问题,因为你需要从容器的头部弹出元素),更改迭代器版本将花费更少的精力。
答案 7 :(得分:1)
如果你在一个范围内创建,使用和销毁一个向量,那么使用迭代器抽象并没有多大好处,但对于更丰富的容器,例如,std::map
,开销变得值得。
迭代器非常适合泛型编程。机器和簿记都没有掩盖代码的意图,你获得了很大的灵活性。想把一个矢量复制到另一个吗?
std::vector<int> v1, v2;
// ...
std::copy(v1.begin(), v1.end(), v2.begin());
如何改为std::cout
?
std::ostream_iterator<int> output(std::cout, " ");
std::copy(v1.begin(), v2.end(), output);
迭代器允许单个模板处理各种情况。
答案 8 :(得分:0)
Personaly,我更喜欢使用迭代的迭代器。显示意图比通过索引,恕我直言访问更好,并且编码习语对于不同的容器是相同的。
答案 9 :(得分:0)
总是有两种想法。对于计算机来说无关紧要,编译器足够大,可以自我管理并最终为这两种情况生成同样好的代码。
但程序员怎么样?
看到for(int i=0;i<blah.size();i++)
我的眼睛立刻将其视为“循环所有元素”
看到了:
typedef std::vector<int> MyIndexes;
MyIndexes indexes;
for (Something::iterator iter = indexes.begin(); iter != indexes.end(); ++iter);
我必须仔细阅读每一行,检查你是不是在做一些棘手的事情。
另一方面,看到for()循环让我担心代码是由一个C程序员编写的,他对STL一无所知,并且设计得非常糟糕。
答案 10 :(得分:0)
标记的答案是错误的。这个代码完全没有“访问”。
在这种情况下,真正的答案应该没有区别,除非向量实现非常无能,除非使用迭代器稍微慢一些。
存储在数组中的迭代器是一个非常愚蠢的概念。它就是这样做的,因为其他一些情况不能直接作为数组加入,所以更一般的是它们鼓励迭代器用于所有东西。尽管迭代器非常繁琐,但只在少数特殊情况下使用才有意义。
答案 11 :(得分:0)
这是一种奇怪的情况,其中通用代码通常比最专业的代码更简单(也更容易编写)。特别是,迭代器类型不是迭代器,而是std::map<std::string, my_type>::iterator
,迭代器类型是一个模板参数,你可以找到方便的名字:
template <class iter>
void my_algorithm(iter a, iter b) {
for (iter i=a; i!=b; ++i)
do_stuff(*i);
}
但我会重复:很多时候,你可以使用现有的算法。对于您引用的案例,似乎std::for_each
(或Boost FOR_EACH
)可能会很好地运作。