迭代还是使用计数器,这就是问题

发布于 2024-08-13 05:47:53 字数 447 浏览 3 评论 0 原文

每当有人开始使用 STL 并且他们有一个向量时,您通常会看到:

vector<int> vec ;

//... code ...

for( vector<int>::iterator iter = vec.begin() ;
     iter != vec.end() ;
     ++iter )
{
  // do stuff
}

我只是发现整个 vector::iterator 语法令人作呕。我知道你可以typedef vector::iterator VecIterInt,并且稍微好一些

但问题是,好的ol'有什么问题:

for( int i = 0 ; i < vec.size() ; i++ )
{
  // code
}

Whenever someone starts using the STL and they have a vector, you usually see:

vector<int> vec ;

//... code ...

for( vector<int>::iterator iter = vec.begin() ;
     iter != vec.end() ;
     ++iter )
{
  // do stuff
}

I just find that whole vector<int>::iterator syntax sickitating. I know you can typedef vector<int>::iterator VecIterInt, and that is slightly better..

But the question is, what's wrong with good ol':

for( int i = 0 ; i < vec.size() ; i++ )
{
  // code
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

谎言月老 2024-08-20 05:47:53

当您使用索引对容器(std::vector 或其他任何内容)执行本质上顺序访问时,您就施加了随机访问要求到底层数据结构,而实际上您在算法中不需要这种访问。与顺序访问的明显较弱的要求相比,随机访问要求是相当强的要求。在没有充分理由的情况下强加更严格的要求是一个重大的设计错误。

因此,您问题的正确答案是:尽可能使用顺序(迭代器)访问,仅在绝对必要时才使用随机(索引)访问。尽可能避免索引访问。

如果您的算法严重依赖于容器可随机访问,那么它就成为算法的外部要求。在这种情况下,您可以毫无保留地使用索引访问。然而,如果可以仅使用迭代器来实现相同的算法,则最好仅坚持使用迭代器,即完全依赖顺序访问。

当然,上述规则虽然正确,但只有在代码通用达到一定程度时才有意义。如果代码的其他部分非常特定,那么您可以确定您正在使用的数据结构是 std::vector 并且始终是 < code>std::vector,那么访问方法就不再重要了。使用您喜欢的任何东西。但是,在顺序访问完全足够的情况下,我仍然会避免索引访问。

When you use index to perform essentially sequential access to a container (std::vector or anything else) you are imposing the random-access requirement onto the underlying data structure, when in fact you don't need this kind of access in your algorithm. Random-access requirement is pretty strong requirement, compared to a significantly weaker requirement of sequential access. Imposing the stronger requirement without a good reason is a major design error.

So the correct answer to your question is: use sequential (iterator) access whenever you can, use random (index) access only when you absolutely have to. Try to avoid index access whenever possible.

If your algorithm critically relies on the container being random-accessible, it becomes the external requirement of the algorithm. In this case you can use index access without any reservations. However, if it is possible to implement the same algorithm using iterators only, it is a good practice to stick to iterators only, i.e. exclusively rely on sequential access.

Of course, the above rule, while true, only makes sense in the code is generic to a certain degree. If some other portion of the code is so specific, that you know for sure that the data structure you are working with is a std::vector and will always be a std::vector, then the access method no longer matters. Use whatever you prefer. However, I would still avoid index access in situations when sequential access is perfectly sufficient.

永不分离 2024-08-20 05:47:53

好吧,当谈到 std::vector 时,我认为在循环中使用下标运算符就很好了,并且在性能方面可能大致相同。当您想要将向量与其他 std 函数(如)一起使用时,使用迭代器的优势就显现出来了。

Well when it comes to std::vector I think using the subscript operator in a loop is just fine, and probably about the same in terms of performance. The advantage to using an iterator comes when you want to use the vector with other std functions like in <algorithms>.

不忘初心 2024-08-20 05:47:53

我认为我的论点不是很强,但我几乎总是使用迭代器版本。

typedef std::vector<int> MyIndexes; // or whatever
MyIndexes indexes;
for (Something::iterator iter = indexes.begin(); iter != indexes.end(); ++iter);

现在,如果我必须将向量更改为列表或类似的内容,我只需更改我的 typedef 即可。这在几次情况下很有用。 auto 关键字会让这更好,但我等不及 C++0x for 循环了:)

I don't think my argument is very strong but I almost always use the iterator version.

typedef std::vector<int> MyIndexes; // or whatever
MyIndexes indexes;
for (Something::iterator iter = indexes.begin(); iter != indexes.end(); ++iter);

Now if I have to change the vector to list or something similar I only have to change my typedef. This was useful on couple of occasions. auto keyword will make this better but I can't wait for C++0x for loop :)

临风闻羌笛 2024-08-20 05:47:53

作为一般规则,迭代器不限于 std::vector,它比索引更有效,因为它们可以访问集合的内部并知道迭代的状态。

对于 std::vector 来说,在启用优化的情况下编译时,循环中的迭代器将减少为指针算术。聪明的编译器可能能够对计数器循环执行相同的操作,并且性能上不应该有任何差异,但一般来说(对于更复杂的集合),您应该假设迭代器将为您提供最快的代码。

As a general rule, not limiting ourselves to std::vector, iterators can be more efficient than indexing, since they have access to the internals of the collection and knows the state of the iteration.

In the case of std::vector, the iterator in the loop will reduce to pointer arithmetic when compiled with optimizations on. A clever compiler might be able to do the same with a counter loop, and there shouldn't be any difference in performance, but in general (for more complex collections) you should assume that an iterator will give you the fastest code.

荒人说梦 2024-08-20 05:47:53

有了 C++0x,你就不会遇到这种困境。您将使用新的 for这实际上是 foreach :)

With C++0x you won't have this dilemma. You'll use the new for which is actually foreach :)

記憶穿過時間隧道 2024-08-20 05:47:53

我建议使用迭代器,因为它们更通用。如果在您的开发周期后期,您决定使用 std::list<> 。会比 std::vector<> 更好(也许你发现你有性能问题,因为你需要从容器的头部弹出元素),改变迭代器版本会花费更少的精力。

I would recommend using the iterators simply because they are more generic. If later in your development cycle, you decide that a std::list<> would be better then a std::vector<> (perhaps you find out you have a performance problem because you need to pop elements from the head of the container), it will take less effort to change the iterator version.

偷得浮生 2024-08-20 05:47:53

如果您在单个范围内创建、使用和销毁向量,则使用迭代器抽象并没有太大好处,但对于更丰富的容器,例如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);

迭代器允许单个模板处理各种情况。

If you create, use, and destroy a vector all within a single scope, there's not much benefit to using the iterator abstraction, but for richer containers, e.g., std::map, the overhead becomes worth it.

Iterators are great for generic programming. None of the machinery and bookkeeping obscure the code's intent, and you get great flexibility. Wanna copy one vector to another?

std::vector<int> v1, v2;
// ...

std::copy(v1.begin(), v1.end(), v2.begin());

How about to std::cout instead?

std::ostream_iterator<int> output(std::cout, " ");
std::copy(v1.begin(), v2.end(), output);

Iterators allow a single template to handle all sorts of cases.

迷乱花海 2024-08-20 05:47:53

我通常对向量使用 for 循环 - 对我来说这是处理此类事情的规范方法。并且不要让任何人告诉您迭代器更快 - 请参阅 使用 vector::iterator 还是使用 at() 迭代 STL 向量哪个更快?

另外,迭代器和 foreach 等结构的极端支持者往往会忽略的一件事是,使用 for 循环,您实际上拥有一个整数索引,除了访问集合元素之外,您还可以执行其他操作,这在各种情况下都非常有用的情况。

但是,如果我要使用 STL 算法,或者迭代其他容器类型(例如 std::map),我当然会使用迭代器。

I normally use a for loop for vectors - to me it is the canonical way of dealing with such things. And don't let anyone tell you iterators are faster - see What's faster, iterating an STL vector with vector::iterator or with at()?.

Also, one thing that extreme proponents of iterators and constructs such as foreach tend to ignore is that with a for-loop you actually have an integer index you can do things with other than access the collection elements, which can be very useful in all sorts of situations.

However, if I were to use an STL algorithm, or iterate over an other container type, such as std::map, I would of course use iterators.

夜吻♂芭芘 2024-08-20 05:47:53

就我个人而言,我更喜欢使用迭代器进行迭代。恕我直言,比通过索引访问更好地显示意图,并且不同容器的编码习惯是相同的。

Personaly, I prefer using an iterator for iterating. Shows the intent better than accessing by index, IMHO, and the coding idiom is the same for different containers.

浅沫记忆 2024-08-20 05:47:53

对此总是有两种想法。对于计算机来说,这并不重要,编译器足够大,可以照顾自己,并且最终会为这两种情况生成同样好的代码。

但是程序员呢?

看到 for(int i=0;i 我的眼睛立即将其解读为“循环所有元素”。
在查看时:

typedef std::vector<int> MyIndexes; 
MyIndexes indexes;
for (Something::iterator iter = indexes.begin(); iter != indexes.end(); ++iter);

我必须仔细阅读每一行,以检查您是否在做一些棘手的事情。

另一方面,看到 for() 循环让我担心代码是由对 STL 一无所知的 C 程序员编写的,并且设计得非常糟糕。

Always in two minds about this. To the computer it doesn't matter, the compiler is big enough to look after itself and will end up generating equally good code for either case.

But what about the programmer?

Seeing for(int i=0;i<blah.size();i++) my eye immediately reads this as "loop over all elements".
While seeing:

typedef std::vector<int> MyIndexes; 
MyIndexes indexes;
for (Something::iterator iter = indexes.begin(); iter != indexes.end(); ++iter);

I have to careful read each line to check that you aren't doing something tricky.

On the other hand seeing the for() loop makes me worry that the code is written by a C programmer who knows nothing about the STL and is going to be horribly designed.

稀香 2024-08-20 05:47:53

标记的答案是错误的。这段代码根本没有发生“访问”。

在这种情况下,真正的答案应该没有什么区别,除非向量实现非常无能,除了使用迭代器会稍微慢一些。

存储在数组中的东西的迭代器是一个非常愚蠢的概念。之所以这样做,是因为其他一些情况无法直接作为数组访问,因此更一般地说,他们鼓励对所有内容使用迭代器。尽管迭代器非常麻烦,并且只有在少数特殊情况下才有意义。

The flagged answer is wrong. There's no 'access' going on is this code at all.

The real answer should be no difference in this case unless the vector implementation is exceedingly incompetent, except using an iterator will be slightly slower.

Iterator for something that is stored in an array is quite a silly concept. It's just done that way because some other cases can't be accesed directly as an array, so to be more general they encourage iterator use for everything. Even though iterators are ridiculously cumbersome, and only make sense to use in a few special cases.

箹锭⒈辈孓 2024-08-20 05:47:53

在我看来,使用任何一种都没有抓住重点。迭代集合应该在算法中完成。很多时候,现有的算法可以很好地完成这项工作,但是当它不能很好地完成这项工作时,您通常最好自己编写一些实际上可以用作通用算法的东西。

这是相当奇怪的情况之一,其中通用代码通常比最专业的代码更简单(并且更容易编写)。特别是,迭代器类型不是像 std::map::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)可能会很好地工作。

IMO, using either one is pretty much missing the point. Iterating over a collection should be done in an algorithm. Quite a bit of the time, an existing algorithm will do the job quite nicely, but when it doesn't, you're generally best off writing something yourself that's actually usable as a generic algorithm.

This is one of those rather strange cases where the generic code is often simpler (and easier to write) than the most specialized code. In particular, instead of the iterator being something like std::map<std::string, my_type>::iterator, the iterator type is a template parameter with whatever name you find convenient:

template <class iter>
void my_algorithm(iter a, iter b) { 
    for (iter i=a; i!=b; ++i) 
        do_stuff(*i); 
}

But I'll repeat: quite a bit of the time, you can use an existing algorithm. For the case you cited, it appears that std::for_each (or Boost FOR_EACH) will probably work quite nicely.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文