创建我自己的迭代器


141

我正在尝试学习C ++,所以如果这个问题表明缺乏基础知识,请原谅我,事实是,我缺乏基础知识。

我需要一些帮助,以了解如何为自己创建的类创建迭代器。

我有一个“形状”类,其中包含点容器。我有一个“ Piece”类,它引用Shape并定义Shape的位置。零件没有形状,它仅引用形状。

我希望它看起来像Piece是一个点的容器,这些容器与它引用的Shape相同,但是增加了Piece位置的偏移量。

我希望能够遍历Piece的点,就好像Piece本身是一个容器一样。我已经读了一些书,却没有发现任何对我有帮助的东西。我将不胜感激任何指针。


6
发布示例代码将有助于描述您所做的工作,而不只是纯英文文本。
格雷格·罗杰斯

3
创建自定义迭代器可能不是一个基础的顶层,至少是中间的。
ldog 2012年

Answers:


41

您应该使用Boost.Iterators。它包含许多模板和概念,用于为现有迭代器实现新的迭代器和适配器。我写了一篇关于这个话题的文章 ; 刊登在2008年12月的ACCU杂志上。它讨论了一种精确解决您问题的(IMO)优雅解决方案:使用Boost.Iterators从对象公开成员集合。

如果只想使用stl,则Josuttis书中有一章介绍了如何实现自己的STL迭代器。


3
只是个小小的提法:这本书讲的是C ++标准库,而不是STL,尽管它们是不同的,但是很困惑(我也感到内/)
CppChris

62

/ EDIT:我知道,这里实际上需要一个自己的迭代器(我先错读了问题)。尽管如此,我还是保留下面的代码,因为它在类似情况下很有用。


这里实际上是否需要一个自己的迭代器?将所有必需的定义转发到保存实际点的容器也许就足够了:

// Your class `Piece`
class Piece {
private:
    Shape m_shape;

public:

    typedef std::vector<Point>::iterator iterator;
    typedef std::vector<Point>::const_iterator const_iterator;

    iterator begin() { return m_shape.container.begin(); }

    const_iterator begin() const { return m_shape.container.begin(); }

    iterator end() { return m_shape.container.end(); }

    const_iterator end() const { return m_shape.const_container.end(); }
}

这是假设您在vector内部使用,但是类型可以轻松调整。


也许他想针对他的班级使用STL算法或功能特性...
gbjbaanb

2
最初的问题确实是说,计件容器的迭代器在返回值时应修改值。这将需要一个单独的迭代器,尽管它可能应该继承或以其他方式从原始对象中获取。
workmad3

@gbjbaanb:关于我的代码的好处是,它可以通过STL算法中使用。
Konrad Rudolph

1
几年后,这仍然是google上最好的结果之一...现在可以通过执行以下操作来概括这一点:auto begin() -> decltype(m_shape.container.begin()) { return m_shape.container.begin(); }
user2962533

20

在这里,像自定义容器一样设计STL是一篇很棒的文章,它解释了一些如何像容器类的STL及其迭代器类一起设计的基本概念。反向迭代器(难度较小)虽然作为练习而保留:-)

HTH,



2

用C ++编写自定义迭代器可能很冗长且复杂。

由于我找不到写自定义迭代器的最小方法,因此我写了可能有用的此模板头。例如,使Piece类可迭代:

#include <iostream>
#include <vector>

#include "iterator_tpl.h"

struct Point {
  int x;
  int y;
  Point() {}
  Point(int x, int y) : x(x), y(y) {}
  Point operator+(Point other) const {
    other.x += x;
    other.y += y;
    return other;
  }
};

struct Shape {
  std::vector<Point> vec;
};

struct Piece {
  Shape& shape;
  Point offset;
  Piece(Shape& shape, int x, int y) : shape(shape), offset(x,y) {}

  struct it_state {
    int pos;
    inline void next(const Piece* ref) { ++pos; }
    inline void begin(const Piece* ref) { pos = 0; }
    inline void end(const Piece* ref) { pos = ref->shape.vec.size(); }
    inline Point get(Piece* ref) { return ref->offset + ref->shape.vec[pos]; }
    inline bool cmp(const it_state& s) const { return pos != s.pos; }
  };
  SETUP_ITERATORS(Piece, Point, it_state);
};

然后,您就可以将其用作普通的STL容器:

int main() {
  Shape shape;
  shape.vec.emplace_back(1,2);
  shape.vec.emplace_back(2,3);
  shape.vec.emplace_back(3,4);

  Piece piece(shape, 1, 1);

  for (Point p : piece) {
    std::cout << p.x << " " << p.y << std::endl;
    // Output:
    // 2 3
    // 3 4
    // 4 5
  }

  return 0;
}

它还允许添加其他类型的迭代器,例如const_iteratorreverse_const_iterator

希望对您有所帮助。


1

解决问题的方法不是创建自己的迭代器,而是使用现有的STL容器和迭代器。将每个形状中的点存储在类似矢量的容器中。

class Shape {
    private:
    vector <Point> points;

从那时起您所做的工作取决于您的设计。最好的方法是遍历Shape内部方法中的点。

for (vector <Point>::iterator i = points.begin(); i != points.end(); ++i)
    /* ... */

如果需要访问Shape外部的点(这可能是设计不足的标志),则可以在Shape方法中创建,该方法将返回点的迭代器访问函数(在这种情况下,还为点容器创建公共typedef)。有关此方法的详细信息,请查看Konrad Rudolph的答案。


3
他仍然需要制作自己的迭代器,以将请求转发给Piece到该Piece中的Shapes。自定义迭代器是一个很好的工具,使用起来非常优雅。
Roel
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.