【问题标题】:Iterators.. why use them? [duplicate]迭代器..为什么要使用它们? [复制]
【发布时间】:2010-09-15 18:56:11
【问题描述】:

在 STL 库中,一些容器具有迭代器,通常认为它们是遍历这些容器的一种更好的方式,而不是简单的 for 循环,例如

for ( int i=0; i < vecVector.size(); i++ )
{

..

}

谁能告诉我为什么以及在什么情况下我应该使用迭代器以及在什么情况下上面的代码 sn-p?

【问题讨论】:

  • 哪些 STL 容器没有有迭代器?
  • 嗯,根据 STL,如果一个类型具有关联的迭代器类型,那么它就是一个容器,因此根据定义,没有。

标签: c++ stl iterator


【解决方案1】:

请注意,vector 的通常实现不会使用“int”作为索引/大小的类型。所以你的代码至少会引发编译器警告。

通用性

迭代器增加了代码的通用性。

例如:

typedef std::vector<int> Container ;

void doSomething(Container & p_aC)
{
    for(Container::iterator it = p_aC.begin(), itEnd = p_aC.end(); it != itEnd; ++it)
    {
       int & i = *it ; // i is now a reference to the value iterated
       // do something with "i"
    }
}

现在,假设您将向量更改为列表(因为在您的情况下,列表现在更好)。只需要修改typedef声明,重新编译代码即可。

如果您改为使用基于索引的代码,则需要重新编写。

访问

迭代器应该被视为一种超级指针。 它“指向”值(或者,在映射的情况下,指向键/值对)。

但它具有移动到容器中下一项的方法。或者上一个。有些容器甚至提供随机访问(向量和双端队列)。

算法

大多数 STL 算法都适用于迭代器或迭代器范围(同样,因为通用性)。您将无法在此处使用索引。

【讨论】:

  • 注意:这段代码在'range'库中特别强大。除了流和其他值生成器之外,在迭代器对上工作的算法可以与来自容器的子集一起使用。请参阅boost.org、“Range”和“Iterators”库。
【解决方案2】:

迭代器大多是更高层次的抽象。

你的 sn-p 假设容器可以被索引。这适用于 std::vector&lt;&gt; 和其他一些容器,例如原始数组。

std::set&lt;&gt; 完全缺乏索引,std::map&lt;&gt; 的索引运算符会将提供给它的任何参数插入到映射中 - 而不是 for 循环中的预期行为。

此外,只有经过衡量和证明,性能问题才会成为问题。

【讨论】:

    【解决方案3】:

    如果您使用迭代器作为函数的参数,您可以将其与所使用的“容器”类型分离。例如,您可以将函数的结果定向到控制台输出而不是向量(下面的示例)。这个技巧对于减少类之间的耦合非常有效。松散耦合的类更容易测试。

    #include <iostream>
    #include <vector>
    #include <algorithm>
    
    using namespace std;
    
    template <typename InputIterator, typename OutputIterator>
    void AddOne(InputIterator begin, InputIterator end, OutputIterator dest)
    {
        while (begin != end)
        {
            *dest = *begin + 1;
            ++dest;
            ++begin;
        }
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        vector<int> data;
        data.push_back(1);
        data.push_back(2);
        data.push_back(3);
    
        // Compute intermediate results vector and dump to console
        vector<int> results;
        AddOne(data.begin(), data.end(), back_inserter(results));
        copy(results.begin(), results.end(), ostream_iterator<int>(cout, " "));
        cout << endl;
    
        // Compute results and send directly to console, no intermediate vector required
        AddOne(data.begin(), data.end(), ostream_iterator<int>(cout, " "));
        cout << endl;
    
        return 0;
    }
    

    【讨论】:

      【解决方案4】:

      好吧,一方面,如果您将该向量转换为列表,上述内容将不再有效。

      迭代器允许您创建不需要知道它们工作的容器类型的函数模板。您甚至可以执行以下操作:

      #include <algorithm>
      
      void printvalue(double s)
      {
          // Do something with s
      }
      
      int _tmain(int argc, _TCHAR* argv[])
      {
          double s[20] = {0};
      
          std::for_each(s, s+20, printvalue);
      
          return 0;
      }
      

      这是因为标准指针也是 for_each 的有效迭代器。

      戴夫

      【讨论】:

        【解决方案5】:

        在您的示例中,对 vecVector.size() 的调用效率低于使用迭代器。迭代器本质上使您不必担心被迭代的容器的大小。此外,迭代器不必按顺序进行。它只需要以它认为合适的任何方式响应 .next 调用。

        【讨论】:

        • 我不认为它的效率较低。编译器将通过将对 size() 的调用放在循环之外来优化它。此外,大小是向量的属性,总是已知的,永远不需要计算,就像 end() 一样。
        • 如果一个项目被添加到循环内的向量中?
        • 那么当然,它不会在循环外优化它,但它也不会在循环外使用 end() 进行优化。所以还是没有区别。
        【解决方案6】:

        使用迭代器可以让您的代码与容器的实现无关。如果您的容器的随机访问成本很低,那么在性能方面并没有太大差异。

        但在很多情况下,您不知道是否是这种情况。如果您尝试在链接列表上使用您的方法,例如,使用下标,容器将不得不在每次迭代时遍历列表以找到您的元素。

        因此,除非您确定对容器的随机访问很便宜,否则请使用迭代器。

        【讨论】:

        • 我认为 std::list 没有订阅运算符。
        • 是的,该集合甚至可能不支持随机访问——因此订阅甚至可能不起作用。迭代器无论如何都可以工作。
        猜你喜欢
        • 2012-03-23
        • 2013-04-06
        • 2016-08-01
        • 2010-11-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-07
        • 1970-01-01
        相关资源
        最近更新 更多