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

发布于 2024-07-05 17:49:12 字数 299 浏览 9 评论 0原文

采取以下两行代码:

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
}

我被告知第二种方式是首选。 到底是为什么呢?

Take the following two lines of code:

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

And this:

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

I'm told that the second way is preferred. Why exactly is this?

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

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

发布评论

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

评论(27

转瞬即逝 2024-07-12 17:49:12
  • 如果您喜欢接近金属/不信任其实现细节,请不要使用迭代器。
  • 如果您在开发过程中定期将一种集合类型切换为另一种集合类型,请使用迭代器。
  • 如果您发现很难记住如何迭代不同类型的集合(也许您正在使用来自多个不同外部源的多种类型),请使用迭代器来统一遍历元素的方法。 这适用于交换链表和数组列表。

真的,这就是全部了。 平均来说,无论哪种方式,你都不会获得更多的简洁性,如果简洁确实是你的目标,你总是可以依靠宏。

  • If you like being close to the metal / don't trust their implementation details, don't use iterators.
  • If you regularly switch out one collection type for another during development, use iterators.
  • If you find it difficult to remember how to iterate different sorts of collections (maybe you have several types from several different external sources in use), use iterators to unify the means by which you walk over elements. This applies to say switching a linked list with an array list.

Really, that's all there is to it. It's not as if you're going to gain more brevity either way on average, and if brevity really is your goal, you can always fall back on macros.

谜兔 2024-07-12 17:49:12

比“告诉 CPU 做什么”(命令式)更好的是“告诉库你想要什么”(功能性)。

因此,您应该学习 stl 中的算法,而不是使用循环。

Even better than "telling the CPU what to do" (imperative) is "telling the libraries what you want" (functional).

So instead of using loops you should learn the algorithms present in stl.

泼猴你往哪里跑 2024-07-12 17:49:12

我不使用迭代器的原因与我不喜欢 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] ...


  }

我什至不想将“animation_matrices[i]”之类的东西缩写为一些随机的“anim_matrix”命名迭代器,因为那样你就不能清楚地看到该值源自哪个数组。

I don't use iterators for the same reason I dislike foreach-statements. When having multiple inner-loops it's hard enough to keep track of global/member variables without having to remember all the local values and iterator-names as well. What I find useful is to use two sets of indices for different occasions:

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] ...


  }

I don't even want to abbreviate things like "animation_matrices[i]" to some random "anim_matrix"-named-iterator for example, because then you can't see clearly from which array this value is originated.

还如梦归 2024-07-12 17:49:12

我觉得这里的答案都没有解释为什么我喜欢将迭代器作为一个通用概念而不是索引到容器中。 请注意,我使用迭代器的大部分经验实际上并非来自 C++,而是来自 Python 等高级编程语言。

迭代器接口对函数的使用者提出了更少的要求,这使得使用者可以用它做更多的事情。

如果您需要的只是能够前向迭代,那么开发人员不仅限于使用可索引容器 - 他们可以使用任何实现 operator++(T&)operator*(T )operator!=(const &T, const &T)

#include <iostream>
template <class InputIterator>
void printAll(InputIterator& begin, InputIterator& end)
{
    for (auto current = begin; current != end; ++current) {
        std::cout << *current << "\n";
    }
}

// elsewhere...

printAll(myVector.begin(), myVector.end());

您的算法适用于您需要它的情况 - 迭代向量 - 但它对于您不一定预期的应用程序也很有用:

#include <random>

class RandomIterator
{
private:
    std::mt19937 random;
    std::uint_fast32_t current;
    std::uint_fast32_t floor;
    std::uint_fast32_t ceil;

public:
    RandomIterator(
        std::uint_fast32_t floor = 0,
        std::uint_fast32_t ceil = UINT_FAST32_MAX,
        std::uint_fast32_t seed = std::mt19937::default_seed
    ) :
        floor(floor),
        ceil(ceil)
    {
        random.seed(seed);
        ++(*this);
    }

    RandomIterator& operator++()
    {
        current = floor + (random() % (ceil - floor));
    }

    std::uint_fast32_t operator*() const
    {
        return current;
    }

    bool operator!=(const RandomIterator &that) const
    {
        return current != that.current;
    }
};

int main()
{
    // roll a 1d6 until we get a 6 and print the results
    RandomIterator firstRandom(1, 7, std::random_device()());
    RandomIterator secondRandom(6, 7);
    printAll(firstRandom, secondRandom);

    return 0;
}

尝试实现一个方括号运算符,该运算符执行与此迭代器类似的操作,而迭代器的实现相对简单。 方括号运算符还对您的类的功能产生影响 - 您可以索引到任意点 - 这可能难以实现或效率低下。

迭代器还适合装饰。 人们可以编写迭代器,在构造函数中接受迭代器并扩展其功能:

template<class InputIterator, typename T>
class FilterIterator
{
private:
    InputIterator internalIterator;

public:
    FilterIterator(const InputIterator &iterator):
        internalIterator(iterator)
    {
    }

    virtual bool condition(T) = 0;

    FilterIterator<InputIterator, T>& operator++()
    {
        do {
            ++(internalIterator);
        } while (!condition(*internalIterator));

        return *this;
    }

    T operator*()
    {
        // Needed for the first result
        if (!condition(*internalIterator))
            ++(*this);
        return *internalIterator;
    }

    virtual bool operator!=(const FilterIterator& that) const
    {
        return internalIterator != that.internalIterator;
    }
};

template <class InputIterator>
class EvenIterator : public FilterIterator<InputIterator, std::uint_fast32_t>
{
public:
    EvenIterator(const InputIterator &internalIterator) :
        FilterIterator<InputIterator, std::uint_fast32_t>(internalIterator)
    {
    }

    bool condition(std::uint_fast32_t n)
    {
        return !(n % 2);
    }
};


int main()
{
    // Rolls a d20 until a 20 is rolled and discards odd rolls
    EvenIterator<RandomIterator> firstRandom(RandomIterator(1, 21, std::random_device()()));
    EvenIterator<RandomIterator> secondRandom(RandomIterator(20, 21));
    printAll(firstRandom, secondRandom);

    return 0;
}

虽然这些玩具可能看起来很平常,但不难想象使用迭代器和迭代器装饰器通过简单的接口来完成强大的事情 - 装饰数据库的只进迭代器例如,使用迭代器从单个结果构造模型对象。 这些模式可以实现无限集的内存高效迭代,并且通过像我上面写的那样的过滤器,可以对结果进行潜在的惰性评估。

C++ 模板的部分功能在于迭代器接口,当应用于固定长度的 C 数组等时,衰减为简单高效的指针算术,使其成为真正的零成本抽象。

I felt that none of the answers here explain why I like iterators as a general concept over indexing into containers. Note that most of my experience using iterators doesn't actually come from C++ but from higher-level programming languages like Python.

The iterator interface imposes fewer requirements on consumers of your function, which allows consumers to do more with it.

If all you need is to be able to forward-iterate, the developer isn't limited to using indexable containers - they can use any class implementing operator++(T&), operator*(T) and operator!=(const &T, const &T).

#include <iostream>
template <class InputIterator>
void printAll(InputIterator& begin, InputIterator& end)
{
    for (auto current = begin; current != end; ++current) {
        std::cout << *current << "\n";
    }
}

// elsewhere...

printAll(myVector.begin(), myVector.end());

Your algorithm works for the case you need it - iterating over a vector - but it can also be useful for applications you don't necessarily anticipate:

#include <random>

class RandomIterator
{
private:
    std::mt19937 random;
    std::uint_fast32_t current;
    std::uint_fast32_t floor;
    std::uint_fast32_t ceil;

public:
    RandomIterator(
        std::uint_fast32_t floor = 0,
        std::uint_fast32_t ceil = UINT_FAST32_MAX,
        std::uint_fast32_t seed = std::mt19937::default_seed
    ) :
        floor(floor),
        ceil(ceil)
    {
        random.seed(seed);
        ++(*this);
    }

    RandomIterator& operator++()
    {
        current = floor + (random() % (ceil - floor));
    }

    std::uint_fast32_t operator*() const
    {
        return current;
    }

    bool operator!=(const RandomIterator &that) const
    {
        return current != that.current;
    }
};

int main()
{
    // roll a 1d6 until we get a 6 and print the results
    RandomIterator firstRandom(1, 7, std::random_device()());
    RandomIterator secondRandom(6, 7);
    printAll(firstRandom, secondRandom);

    return 0;
}

Attempting to implement a square-brackets operator which does something similar to this iterator would be contrived, while the iterator implementation is relatively simple. The square-brackets operator also makes implications about the capabilities of your class - that you can index to any arbitrary point - which may be difficult or inefficient to implement.

Iterators also lend themselves to decoration. People can write iterators which take an iterator in their constructor and extend its functionality:

template<class InputIterator, typename T>
class FilterIterator
{
private:
    InputIterator internalIterator;

public:
    FilterIterator(const InputIterator &iterator):
        internalIterator(iterator)
    {
    }

    virtual bool condition(T) = 0;

    FilterIterator<InputIterator, T>& operator++()
    {
        do {
            ++(internalIterator);
        } while (!condition(*internalIterator));

        return *this;
    }

    T operator*()
    {
        // Needed for the first result
        if (!condition(*internalIterator))
            ++(*this);
        return *internalIterator;
    }

    virtual bool operator!=(const FilterIterator& that) const
    {
        return internalIterator != that.internalIterator;
    }
};

template <class InputIterator>
class EvenIterator : public FilterIterator<InputIterator, std::uint_fast32_t>
{
public:
    EvenIterator(const InputIterator &internalIterator) :
        FilterIterator<InputIterator, std::uint_fast32_t>(internalIterator)
    {
    }

    bool condition(std::uint_fast32_t n)
    {
        return !(n % 2);
    }
};


int main()
{
    // Rolls a d20 until a 20 is rolled and discards odd rolls
    EvenIterator<RandomIterator> firstRandom(RandomIterator(1, 21, std::random_device()()));
    EvenIterator<RandomIterator> secondRandom(RandomIterator(20, 21));
    printAll(firstRandom, secondRandom);

    return 0;
}

While these toys might seem mundane, it's not difficult to imagine using iterators and iterator decorators to do powerful things with a simple interface - decorating a forward-only iterator of database results with an iterator which constructs a model object from a single result, for example. These patterns enable memory-efficient iteration of infinite sets and, with a filter like the one I wrote above, potentially lazy evaluation of results.

Part of the power of C++ templates is your iterator interface, when applied to the likes of fixed-length C arrays, decays to simple and efficient pointer arithmetic, making it a truly zero-cost abstraction.

本宫微胖 2024-07-12 17:49:12

两种实现都是正确的,但我更喜欢“for”循环。 由于我们决定使用 Vector 而不是任何其他容器,因此使用索引将是最佳选择。 将迭代器与向量一起使用会失去将对象放在连续内存块中的好处,而这有助于简化对象的访问。

Both the implementations are correct, but I would prefer the 'for' loop. As we have decided to use a Vector and not any other container, using indexes would be the best option. Using iterators with Vectors would lose the very benefit of having the objects in continuous memory blocks which help ease in their access.

撑一把青伞 2024-07-12 17:49:12

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

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

}

I always use array index because many application of mine require something like "display thumbnail image". So I wrote something like this:

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

}
老娘不死你永远是小三 2024-07-12 17:49:12

对于容器独立性

For container independence

难得心□动 2024-07-12 17:49:12

已经有几个优点了。 我有一些额外的评论:

  1. 假设我们正在谈论 C++ 标准库,“向量”意味着一个具有 C 数组保证的随机访问容器(随机访问、连续内存布局等)。 如果您说“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 像容器一样,没有什么好处。优先选择其中一种的原因是,如果您必须经常引用旧的或新的元素索引,那么索引版本更合适。

一般来说,首选使用迭代器,因为算法会使用它们和行为。可以通过更改迭代器的类型来控制(并隐式记录)。可以使用数组位置代替迭代器,但语法差异将很明显。

Several good points already. I have a few additional comments:

  1. Assuming we are talking about the C++ standard library, "vector" implies a random access container that has the guarantees of C-array (random access, contiguos memory layout etc). If you had said 'some_container', many of the above answers would have been more accurate (container independence etc).

  2. To eliminate any dependencies on compiler optimization, you could move some_vector.size() out of the loop in the indexed code, like so:

    const size_t numElems = some_vector.size();
    for (size_t i = 0; i 
  3. Always pre-increment iterators and treat post-increments as exceptional cases.

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

So assuming and indexable std::vector<> like container, there is no good reason to prefer one over other, sequentially going through the container. If you have to refer to older or newer elemnent indexes frequently, then the indexed version is more appropropriate.

In general, using the iterators is preferred because algorithms make use of them and behavior can be controlled (and implicitly documented) by changing the type of the iterator. Array locations can be used in place of iterators, but the syntactical difference will stick out.

明天过后 2024-07-12 17:49:12

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

During iteration you don't need to know number of item to be processed. You just need the item and iterators do such things very good.

绻影浮沉 2024-07-12 17:49:12

目前还没有人提到索引的一个优点是,当您附加到诸如 std::vector 之类的连续容器时,它们不会变得无效,因此您可以在迭代期间向容器添加项目。

这也可以通过迭代器实现,但您必须调用 reserve(),因此需要知道要追加多少项。

No one mentioned yet that one advantage of indices is that they are not become invalid when you append to a contiguous container like std::vector, so you can add items to the container during iteration.

This is also possible with iterators, but you must call reserve(), and therefore need to know how many items you'll append.

无远思近则忧 2024-07-12 17:49:12

如果您有权访问 C++11 功能,那么您还可以使用基于范围的 for 循环 for如下迭代您的向量(或任何其他容器):

for (auto &item : some_vector)
{
     //do stuff
}

此循环的好处是您可以通过 item 变量直接访问向量的元素,而不会冒弄乱索引或取消引用迭代器时犯了一个错误。 此外,占位符 auto 会阻止您必须重复容器元素的类型,
这使您更接近独立于容器的解决方案。

注意:

  • 如果您的循环中需要元素索引,并且您的容器存在 operator[] (并且对您来说足够快),那么最好采用第一种方法。
  • 基于范围的 for 循环不能用于向容器添加元素或从容器中删除元素。 如果您想这样做,那么最好坚持使用 Brian Matthews 提供的解决方案
  • 如果您不想更改容器中的元素,则应使用关键字 const,如下所示:for (auto const &item : some_vector) { ... }< /代码>。

If you have access to C++11 features, then you can also use a range-based for loop for iterating over your vector (or any other container) as follows:

for (auto &item : some_vector)
{
     //do stuff
}

The benefit of this loop is that you can access elements of the vector directly via the item variable, without running the risk of messing up an index or making a making a mistake when dereferencing an iterator. In addition, the placeholder auto prevents you from having to repeat the type of the container elements,
which brings you even closer to a container-independent solution.

Notes:

  • If you need the the element index in your loop and the operator[] exists for your container (and is fast enough for you), then better go for your first way.
  • A range-based for loop cannot be used to add/delete elements into/from a container. If you want to do that, then better stick to the solution given by Brian Matthews.
  • If you don't want to change the elements in your container, then you should use the keyword const as follows: for (auto const &item : some_vector) { ... }.
つ低調成傷 2024-07-12 17:49:12

第二种形式更准确地代表您正在做的事情。 在您的示例中,您实际上并不关心 i 的值 - 您想要的只是迭代器中的下一个元素。

The second form represents what you're doing more accurately. In your example, you don't care about the value of i, really - all you want is the next element in the iterator.

飞烟轻若梦 2024-07-12 17:49:12

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

Indexing requires an extra mul operation. For example, for vector<int> v, the compiler converts v[i] into &v + sizeof(int) * i.

你另情深 2024-07-12 17:49:12

在对这个答案的主题有了更多了解之后,我意识到这有点过于简单化了。 之间的区别

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 算法库结合使用时,您可以做一些超出数组索引管理范围的非常酷的事情。

After having learned a little more on the subject of this answer, I realize it was a bit of an oversimplification. The difference between this loop:

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

And this loop:

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

Is fairly minimal. In fact, the syntax of doing loops this way seems to be growing on me:

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

Iterators do unlock some fairly powerful declarative features, and when combined with the STL algorithms library you can do some pretty cool things that are outside the scope of array index administrivia.

别念他 2024-07-12 17:49:12

我认为这对于向量来说没有太大区别。 我更喜欢自己使用索引,因为我认为它更具可读性,并且您可以进行随机访问,例如向前跳转 6 个项目或根据需要向后跳转。

我还喜欢像这样引用循环内的项目,因此该位置周围没有很多方括号:

for(size_t i = 0; i < myvector.size(); i++)
{
    MyClass &item = myvector[i];

    // Do stuff to "item".
}

如果您认为在某些时候可能需要用列表替换向量,那么使用迭代器可能会很好在未来,它对于 STL 怪胎来说也看起来更时尚,但我想不出任何其他原因。

I don't think it makes much difference for a vector. I prefer to use an index myself as I consider it to be more readable and you can do random access like jumping forward 6 items or jumping backwards if needs be.

I also like to make a reference to the item inside the loop like this so there are not a lot of square brackets around the place:

for(size_t i = 0; i < myvector.size(); i++)
{
    MyClass &item = myvector[i];

    // Do stuff to "item".
}

Using an iterator can be good if you think you might need to replace the vector with a list at some point in the future and it also looks more stylish to the STL freaks but I can't think of any other reason.

少钕鈤記 2024-07-12 17:49:12

STL 迭代器大多存在,因此像排序这样的 STL 算法可以独立于容器。

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

对于大多数人来说,它的打字量更少,更容易解析。 如果 C++ 有一个简单的 foreach 循环而不过度使用模板魔法,那就太好了。

for( size_t i = 0; i < some_vector.size(); ++i )
{
   T& rT = some_vector[i];
   // now do something with rT
}
'

STL iterators are mostly there so that the STL algorithms like sort can be container independent.

If you just want to loop over all the entries in a vector just use the index loop style.

It is less typing and easier to parse for most humans. It would be nice if C++ had a simple foreach loop without going overboard with template magic.

for( size_t i = 0; i < some_vector.size(); ++i )
{
   T& rT = some_vector[i];
   // now do something with rT
}
'
[旋木] 2024-07-12 17:49:12

除了所有其他出色的答案之外... 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
}

Aside from all of the other excellent answers... int may not be large enough for your vector. Instead, if you want to use indexing, use the size_type for your container:

for (std::vector<Foo>::size_type i = 0; i < myvector.size(); ++i)
{
    Foo& this_foo = myvector[i];
    // Do stuff with this_foo
}
树深时见影 2024-07-12 17:49:12

我可能应该指出你也可以调用

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

I probably should point out you can also call

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

塔塔猫 2024-07-12 17:49:12

迭代器的另一个好处是它们可以更好地允许您表达(并强制执行)您的 const 偏好。 此示例确保您不会在循环过程中更改向量:


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
}

Another nice thing about iterators is that they better allow you to express (and enforce) your const-preference. This example ensures that you will not be altering the vector in the midst of your loop:


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
}
花开柳相依 2024-07-12 17:49:12

因为它更加面向对象。 如果您使用索引进行迭代,您假设:

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

使用迭代器,您是在说“给我一切,以便我可以使用它”,而不知道底层实现是什么。 (在Java中,有些集合是无法通过索引访问的)

此外,使用迭代器,无需担心超出数组范围。

Because it is more object-oriented. if you are iterating with an index you are assuming:

a) that those objects are ordered
b) that those objects can be obtained by an index
c) that the index increment will hit every item
d) that that index starts at zero

With an iterator, you are saying "give me everything so I can work with it" without knowing what the underlying implementation is. (In Java, there are collections that cannot be accessed through an index)

Also, with an iterator, no need to worry about going out of bounds of the array.

苏别ゝ 2024-07-12 17:49:12

关注点分离

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

事实上,按索引迭代会将您与容器的实现联系起来。 向容器请求开始和结束迭代器,使循环代码能够与其他容器类型一起使用。

另外,以 std::for_each 方式,您告诉集合要做什么,而不是询问它的内部结构

0x 标准将引入闭包,这将使这种方法更易于使用 - 看看 Ruby 的 的表达能力[1..6].each { |i| 打印我; }...

性能

但也许一个备受关注的问题是,使用 for_each 方法产生了并行化迭代的机会 - 英特尔线程块 可以将代码块分布到系统中的多个处理器上!

注意:在发现algorithms库,特别是foreach之后,我花了两三个月的时间编写了小得可笑的“辅助”运算符结构,这会让其他开发人员发疯。 这次之后,我又回到了一种务实的方法 - 小循环体不应该再有 foreach :)

关于迭代器的必读参考文献是这本书 "扩展 STL"

GoF 在迭代器模式的末尾有一个小段落,它讨论了这种迭代的品牌; 它被称为“内部迭代器”。 也可以看看这里

Separation of Concerns

It's very nice to separate the iteration code from the 'core' concern of the loop. It's almost a design decision.

Indeed, iterating by index ties you to the implementation of the container. Asking the container for a begin and end iterator, enables the loop code for use with other container types.

Also, in the std::for_each way, you TELL the collection what to do, instead of ASKing it something about its internals

The 0x standard is going to introduce closures, which will make this approach much more easy to use - have a look at the expressive power of e.g. Ruby's [1..6].each { |i| print i; }...

Performance

But maybe a much overseen issue is that, using the for_each approach yields an opportunity to have the iteration parallelized - the intel threading blocks can distribute the code block over the number of processors in the system!

Note: after discovering the algorithms library, and especially foreach, I went through two or three months of writing ridiculously small 'helper' operator structs which will drive your fellow developers crazy. After this time, I went back to a pragmatic approach - small loop bodies deserve no foreach no more :)

A must read reference on iterators is the book "Extended STL".

The GoF have a tiny little paragraph in the end of the Iterator pattern, which talks about this brand of iteration; it's called an 'internal iterator'. Have a look here, too.

囍笑 2024-07-12 17:49:12

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

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

如果您使用索引,则必须在数组中向上/向下调整项目以处理插入和删除。

You might want to use an iterator if you are going to add/remove items to the vector while you are iterating over it.

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

If you were using indices you would have to shuffle items up/down in the array to handle the insertions and deletions.

紫罗兰の梦幻 2024-07-12 17:49:12

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

所以它提供了两件事:

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

Imagine some_vector is implemented with a linked-list. Then requesting an item in the i-th place requires i operations to be done to traverse the list of nodes. Now, if you use iterator, generally speaking, it will make its best effort to be as efficient as possible (in the case of a linked list, it will maintain a pointer to the current node and advance it in each iteration, requiring just a single operation).

So it provides two things:

  • Abstraction of use: you just want to iterate some elements, you don't care about how to do it
  • Performance
莳間冲淡了誓言ζ 2024-07-12 17:49:12

我将在这里成为魔鬼的拥护者,而不是推荐迭代器。 主要原因是,我从桌面应用程序开发到游戏开发所从事的所有源代码都不需要使用迭代器。 一直以来,它们都不是必需的,其次,迭代器所带来的隐藏假设、代码混乱和调试噩梦使它们成为不要在任何需要速度的应用程序中使用它的典型例子。

即使从维护的角度来看,它们也是一团糟。 这不是因为它们,而是因为幕后发生的所有锯齿。 我怎么知道您还没有实现自己的虚拟向量或数组列表,这些列表的功能与标准完全不同。 我知道运行时当前是什么类型吗? 你是否重载了一个运算符我没有时间检查你所有的源代码。 我到底知道你用的是哪个版本的STL?

迭代器遇到的下一个问题是抽象泄漏,尽管有许多网站与迭代器详细讨论了这个问题。

抱歉,我还没有并且仍然没有看到迭代器的任何意义。 如果他们将列表或向量从您那里抽象出来,而事实上您应该已经知道您正在处理什么向量或列表,如果您不这样做,那么您将为将来的一些出色的调试会话做好准备。

I'm going to be the devils advocate here, and not recommend iterators. The main reason why, is all the source code I've worked on from Desktop application development to game development have i nor have i needed to use iterators. All the time they have not been required and secondly the hidden assumptions and code mess and debugging nightmares you get with iterators make them a prime example not to use it in any applications that require speed.

Even from a maintence stand point they're a mess. Its not because of them but because of all the aliasing that happen behind the scene. How do i know that you haven't implemented your own virtual vector or array list that does something completely different to the standards. Do i know what type is currently now during runtime? Did you overload a operator I didn't have time to check all your source code. Hell do i even know what version of the STL your using?

The next problem you got with iterators is leaky abstraction, though there are numerous web sites that discuss this in detail with them.

Sorry, I have not and still have not seen any point in iterators. If they abstract the list or vector away from you, when in fact you should know already what vector or list your dealing with if you don't then your just going to be setting yourself up for some great debugging sessions in the future.

岛徒 2024-07-12 17:49:12

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

because you are not tying your code to the particular implementation of the some_vector list. if you use array indices, it has to be some form of array; if you use iterators you can use that code on any list implementation.

合约呢 2024-07-12 17:49:12

这是现代 C++ 灌输过程的一部分。 迭代器是迭代大多数容器的唯一方法,因此即使与向量一起使用它也只是为了让自己进入正确的思维模式。 说真的,这是我这样做的唯一原因 - 我不认为我曾经用不同类型的容器替换过向量。


Wow, this is still getting downvoted after three weeks. I guess it doesn't pay to be a little tongue-in-cheek.

我认为数组索引更具可读性。 它与其他语言中使用的语法以及老式 C 数组使用的语法相匹配。 它也不太冗长。 如果你的编译器足够好,那么效率应该是一个很大的问题,而且几乎没有任何情况下它很重要。

即便如此,我仍然发现自己经常将迭代器与向量一起使用。 我相信迭代器是一个重要的概念,所以我会尽可能地推广它。

It's part of the modern C++ indoctrination process. Iterators are the only way to iterate most containers, so you use it even with vectors just to get yourself into the proper mindset. Seriously, that's the only reason I do it - I don't think I've ever replaced a vector with a different kind of container.


Wow, this is still getting downvoted after three weeks. I guess it doesn't pay to be a little tongue-in-cheek.

I think the array index is more readable. It matches the syntax used in other languages, and the syntax used for old-fashioned C arrays. It's also less verbose. Efficiency should be a wash if your compiler is any good, and there are hardly any cases where it matters anyway.

Even so, I still find myself using iterators frequently with vectors. I believe the iterator is an important concept, so I promote it whenever I can.

独留℉清风醉 2024-07-12 17:49:12

仅当 vector.size() 是快速操作时,第一种形式才是有效的。 例如,对于向量来说这是正确的,但对于列表则不然。 另外,您打算在循环体内做什么? 如果您计划像中那样访问元素,

T elem = some_vector[i];

那么您就假设容器已定义 operator[](std::size_t) 。 同样,这对于向量来说是正确的,但对于其他容器则不然。

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

您可以使用标准算法进一步增强您的代码。 根据您想要实现的目标,您可以选择使用 std::for_each()std::transform() 等。 通过使用标准算法而不是显式循环,您可以避免重新发明轮子。 您的代码可能会更高效(假设选择了正确的算法)、正确且可重用。

The first form is efficient only if vector.size() is a fast operation. This is true for vectors, but not for lists, for example. Also, what are you planning to do within the body of the loop? If you plan on accessing the elements as in

T elem = some_vector[i];

then you're making the assumption that the container has operator[](std::size_t) defined. Again, this is true for vector but not for other containers.

The use of iterators bring you closer to container independence. You're not making assumptions about random-access ability or fast size() operation, only that the container has iterator capabilities.

You could enhance your code further by using standard algorithms. Depending on what it is you're trying to achieve, you may elect to use std::for_each(), std::transform() and so on. By using a standard algorithm rather than an explicit loop you're avoiding re-inventing the wheel. Your code is likely to be more efficient (given the right algorithm is chosen), correct and reusable.

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