C++中的自定义迭代器

siotufzp  于 2023-01-22  发布在  其他
关注(0)|答案(7)|浏览(205)

我有一个类TContainer,它是指向TITems类的几个stl集合指针的集合。
我需要创建一个Iterator来遍历TContainer类中所有集合中的元素,从而抽象客户端的内部工作。
做这件事的好方法是什么?我应该创建一个扩展迭代器的类吗(如果是这样,我应该扩展哪个迭代器类),我应该创建一个迭代器集合的迭代器类吗?
我只需要一个FORWARD_ONLY迭代器。
例如,如果这是我的集装箱:

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

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

5vf7fwbs

5vf7fwbs1#

当我创建自己的迭代器时(不久前),我继承了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。

neekobn8

neekobn82#

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

nxowjjhe

nxowjjhe3#

首先,让我们概括一下:

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

uurity8g

uurity8g4#

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

  • 递增和/或递减它
  • 解引用它以获取它“指向”的对象
  • 测试它是否相等和不相等
  • 复制并分配它

一旦你有了一个类,它可以合理地为你的集合做这件事,你将需要修改集合,使其具有返回迭代器的函数。

  • 一个开始()函数,返回位于第一个元素的新迭代器类型的示例
  • 一个end()函数,返回一个迭代器,该迭代器(可能是概念上的)位于容器中项目末尾之后的位置
vuv7lop3

vuv7lop35#

检查Views Template Library
特别检查

  1. Union View表示两个容器连接在一起。
  2. Concatenation View,表示连接的容器集合。
6qqygrtg

6qqygrtg6#

这是我所能生成的最简单的代码(对于自定义迭代器),注意我才刚刚开始探索这一领域,它调用内置的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
swvgeqrz

swvgeqrz7#

除了建议使用Boost.iterator的iterator_facade来实现自定义迭代器之外,我还编写了一个独立的、仅包含头部的库,它模拟了Boost.iterator提供的(重要的)部分,好处是没有Boost依赖(除了C++17之外,也没有任何其他依赖)。
我的库的一个显著特点是,通过使用它,你的迭代器将始终遵循标准对迭代器接口的要求**,而不是更多的**。这确保了下游用户不会依赖于当前迭代器实现的实现细节(将来可能会删除/更改)。这将有助于提高可移植性和可维护性。
您可以在https://github.com/Krzmbrzl/iterators中找到该库。
下面是一个如何用这个库创建自定义迭代器的例子:

#include <iterators/iterator_facade.hpp>

#include <cstddef>
#include <iostream>
#include <iterator>

struct MyCore {
    // Declare what iterator category we are aiming for
    using  target_iterator_category = std::input_iterator_tag;

    // These functions are required for all iterator cores

    MyCore(int *ptr) : m_ptr(ptr) {}
    MyCore(const MyCore &) = default;
    MyCore &operator=(const MyCore &) = default;

    int &dereference() const { return *m_ptr; }

    void increment() { m_ptr += 1; }

    bool equals(const MyCore &other) const { return m_ptr == other.m_ptr; }

private:
    int * m_ptr = nullptr;
};

using MyIterator = iterators::iterator_facade< MyCore >;

// Example usage
int main() {
    int numbers[3] = { 1, 2, 3 };

    MyIterator iter(MyCore{numbers});

    std::cout << *iter << "\n";
    iter++;
    std::cout << *iter << "\n";
    ++iter;
    std::cout << *iter << "\n";

    return 0;
}

相关问题