【问题标题】:Custom Iterator in C++C++ 中的自定义迭代器
【发布时间】:2010-10-24 19:23:14
【问题描述】:

我有一个 TContainer 类,它是几个指向 TItems 类的 stl 集合指针的集合。

我需要创建一个迭代器来遍历我的 TContainer 类中所有集合中的元素,从而抽象出内部工作的客户端。

有什么好的方法可以做到这一点?我应该创建一个扩展迭代器的类(如果是,我应该扩展什么迭代器类),我应该创建一个迭代器聚合的迭代器类吗?

我只需要一个 FORWARD_ONLY 迭代器。

IE,如果这是我的容器:

typedef std::vector <TItem*> ItemVector;
class TContainer {
   std::vector <ItemVector *> m_Items;
};

什么是一个好的迭代器来遍历 m_Items 成员变量的向量中包含的所有项目。

【问题讨论】:

  • 你能告诉我们更多关于你的容器和迭代器的信息吗?例如,迭代器是双向的吗?
  • 谢谢,我编辑了我的问题以澄清您的问题。
  • 您真的希望 m_items 成为指针向量吗?为什么不只是 ItemVector 的向量?
  • 另请参阅:stackoverflow.com/questions/1724009/… 而不是从std::iterator 派生,我建议采用std::iterator_traits 路线:)

标签: c++ stl iterator


【解决方案1】:

当我做自己的迭代器时(不久前),我从 std::iterator 继承并将类型指定为第一个模板参数。希望对您有所帮助。

对于前向迭代器,使用 forward_iterator_tag 而不是以下代码中的 input_iterator_tag。

这个类最初取自 istream_iterator 类(并为我自己的使用而修改,因此它可能不再类似于 istram_iterator)。

template<typename T>
class <PLOP>_iterator
         :public std::iterator<std::input_iterator_tag,       // type of iterator
                               T,ptrdiff_t,const T*,const T&> // Info about iterator
{
    public:
        const T& operator*() const;
        const T* operator->() const;
        <PLOP>__iterator& operator++();
        <PLOP>__iterator operator++(int);
        bool equal(<PLOP>__iterator const& rhs) const;
};

template<typename T>
inline bool operator==(<PLOP>__iterator<T> const& lhs,<PLOP>__iterator<T> const& rhs)
{
    return lhs.equal(rhs);
}

查看有关迭代器标签的文档:
http://www.sgi.com/tech/stl/iterator_tags.html

刚刚重新阅读了有关迭代器的信息:
http://www.sgi.com/tech/stl/iterator_traits.html

这是旧的处理方式 (iterator_tags),更现代的方法是为您的迭代器设置 iterator_traits 以使其与 STL 完全兼容。

【讨论】:

  • 与从 std::iterator 继承相比,您自己的 iterator_traits 特化与 STL 的兼容性如何?
  • std::iterator 具有引用和指针类型的默认参数,因此您不必在示例中键入它们。并且让您的类继承自 std::iterator 会自动为您专门化 iterator_traits,因此它是编写迭代器的现代方式
【解决方案2】:

如果您可以访问 Boost,则使用 iterator_facade 是最可靠的解决方案,而且使用起来非常简单。

【讨论】:

    【解决方案3】:

    首先让我们稍微概括一下:

    typedef int value_type;
    typedef std::vector<value_type*> inner_range;
    typedef std::vector<inner_range*> outer_range;
    

    现在是迭代器:

    struct my_iterator : std::iterator_traits<inner_range::iterator> 
    {
        typedef std::forward_iterator_tag iterator_category;
    
        my_iterator(outer_range::iterator const & outer_iterator, 
                    outer_range::iterator const & outer_end)
        : outer_iterator(outer_iterator), outer_end(outer_end)
        { 
            update();
        }
    
        my_iterator & operator++()
        {
            ++inner_iterator;
            if(inner_iterator == inner_end)
            {
                ++outer_iterator;
                update();
            }
            return *this;
        }
    
        reference operator*() const
        {   
            return *inner_iterator;
        }
    
        bool operator==(my_iterator const & rhs) const
        {   
            bool lhs_end = outer_iterator == outer_end;
            bool rhs_end = rhs.outer_iterator == rhs.outer_end;
            if(lhs_end && rhs_end)
                return true;
            if(lhs_end != rhs_end)
                return false;
            return outer_iterator == rhs.outer_iterator 
                && inner_iterator == rhs.inner_iterator;
        }
    
    private:
    
        outer_range::iterator outer_iterator, outer_end;
        inner_range::iterator inner_iterator, inner_end;
    
        void update()
        {
            while(outer_iterator != outer_end)
            {
                inner_iterator = (*outer_iterator)->begin();
                inner_end = (*outer_iterator)->end();
                if(inner_iterator == inner_end)
                    ++outer_iterator;
                else
                    break;
            }
        }    
    };
    

    此类假设外部迭代器包含指向内部范围的指针,这是您问题中的要求。这反映在update 成员中,在begin()end() 之前的箭头中。如果要在外部迭代器按值包含内部范围的更常见情况下使用此类,可以将这些箭头替换为点。请注意顺便说一句,这个类不知道内部范围包含指针这一事实,只有该类的客户端需要知道这一点。

    如果我们使用boost::iterator_facade,代码可能会更短,但没有必要为如此​​简单的事情添加 boost 依赖项。此外,唯一棘手的部分是相等和递增操作,无论如何我们都必须对其进行编码。

    我留下了以下样板成员作为“读者练习”:

    • 后缀增量迭代器
    • 接线员!=
    • 默认构造函数
    • 操作员->

    另一个有趣的练习是将其转换为可与任意容器一起使用的模板。代码基本相同,只是要在几个地方添加typename注解。

    使用示例:

    int main()
    {
        outer_type outer;
        int a = 0, b = 1, c = 2;
        inner_type inner1, inner2;
        inner1.push_back(&a);
        inner1.push_back(&b);
        inner2.push_back(&c);
        outer.push_back(&inner1);
        outer.push_back(&inner2);
    
        my_iterator it(outer.begin(), outer.end());
                    e(outer.end(), outer.end());
        for(; it != e; ++it)
            std::cout << **it << "\n";
    }
    

    哪些打印:

    0 1 2

    【讨论】:

      【解决方案4】:

      迭代器只是一个支持特定接口的类。至少,您希望能够:

      • 增加和/或减少它
      • 取消引用它以获取它“指向”的对象
      • 测试它的平等和不平等
      • 复制并分配它

      一旦您有了一个可以为您的集合明智地做到这一点的类,您将需要修改集合以具有返回迭代器的函数。至少你会想要

      • 一个 begin() 函数,它返回位于第一个元素的新迭代器类型的实例
      • 一个 end() 函数,它返回一个迭代器,该迭代器(可能在概念上)位于容器中项目的末尾之后

      【讨论】:

      • 它比这更复杂一些。将 Iterator_traits 添加到您的列表中,您就完成了。
      • 你可以使用没有特征的迭代器。
      • 我知道——但很多人从不使用这些算法。我认为人们最好先构建一个非模板版本,而不是从 std::iterator 继承,然后在一切正常后对其进行模板化。当然,其他人可能不同意。
      • 那里的关键字是“你”,但是当您在任何非平凡算法(甚至很多平凡算法)中使用迭代器时,STL 依赖于特征。 Iterator 本身并没有实际定义,它只是一个外行术语,用于描述标准定义的 6 个迭代器概念。这些概念中的每一个都要求提供特定的类型信息。
      【解决方案5】:

      检查Views Template Library

      特别检查

      1. Union View 呈现两个串联的容器。
      2. Concatenation View 展示串联的容器集合。

      【讨论】:

        【解决方案6】:

        这是我能够生成的最简单的代码(用于自定义迭代器)。请注意,我才刚刚开始探索这个领域。这会调用内置的upper_bound函数对整数函数进行二分查找,以x^2为例。

        #include <algorithm>
        #include <iostream>
        
        using namespace std;
        
        class Iter
        {
          public:
          int x;
          Iter() { x = -1; }
          Iter(int a) { x = a; }
        
          bool operator!=(Iter &i2) const { return x != i2.x; }
          void operator++() { x++; }
          void operator+=(int b) { x += b; }
          int operator-(const Iter &i2) const { return x - i2.x; }
          int operator*() const {
            cout << "calculating for x " << x << endl;
            return x*x;
          }
        
          typedef random_access_iterator_tag iterator_category;
          typedef int value_type;
          typedef int difference_type;
          typedef int* pointer;
          typedef int& reference;
        };
        
        main ()
        {
          ios::sync_with_stdio(false);
          cout << upper_bound(Iter(0), Iter(100), 40).x << endl;
        }
        
        // :collapseFolds=1:folding=explicit:
        

        这就是输出的样子:

        calculating for x 50
        calculating for x 25
        calculating for x 12
        calculating for x 6
        calculating for x 9
        calculating for x 8
        calculating for x 7
        7
        

        【讨论】:

          猜你喜欢
          • 2015-11-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-05-13
          • 2015-09-06
          • 2020-12-08
          • 2013-02-17
          • 2015-02-04
          相关资源
          最近更新 更多