创建自己的迭代器

147

我正在尝试学习C ++,所以如果这个问题显示出基础知识的缺乏,请原谅,因为事实上,我确实缺乏基础知识。

我想要一些帮助,想知道如何为我创建的一个类创建迭代器。

我有一个名为“Shape”的类,它具有Points的容器。 我有一个名为“Piece”的类,它引用一个Shape并为该Shape定义位置。 Piece没有Shape,它只是引用了一个Shape。

我希望看起来像Piece是一个包含Points的容器,这些Points与其引用的Shape的Points相同,但偏移了Piece的位置。

我希望能够遍历Piece的Points,就好像Piece本身是一个容器一样。我已经做了一些阅读,没有找到任何有帮助的东西。非常感谢您提供的任何指针。


6
发布示例代码可以更好地描述您正在做的内容,比仅使用纯英文文本更加清晰明了。 - Greg Rogers
3
创建自定义迭代器可能不是一个基础主题,至少属于中级水平。 - ldog
6个回答

63

/编辑:我明白了,实际上在这里需要一个独立的迭代器(我先前误读了问题)。尽管如此,我仍然让下面的代码保留下来,因为它在类似情况下可能是有用的。


这里实际上需要一个独立的迭代器吗?也许将所有必需的定义转发到容纳实际Points的容器中就足够了:

// 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
几年过去了,这仍然是谷歌搜索结果中排名靠前的内容之一... 现在可以通过像这样做来概括: auto begin() -> decltype(m_shape.container.begin()) { return m_shape.container.begin(); } - user2962533

42
你应该使用Boost.Iterators。它包含多个模板和概念,用于实现新迭代器和适配器的现有迭代器。我已经写了一篇关于这个主题的文章; 它在2008年12月的ACCU杂志中。它讨论了一个(在我看来)优雅的解决方案,可以从对象中公开成员集合,使用Boost.Iterators。
如果你只想使用STL,Josuttis书有一章关于实现自己的STL迭代器。

3
只是一小点备注:这本书讲的是C++标准库,而不是STL——它们虽然有所不同,但常常会被混淆(我曾经也犯过这个错误)。 - CppChris

21

这里有一篇绝佳的文章《设计类STL自定义容器》,它解释了如何设计一个类似STL容器的基本概念,包括相应的迭代器类。反向迭代器(稍微难一些)则留作练习。

希望对你有所帮助!


15

您可以阅读这篇ddj文章

基本上,继承自std::iterator可以为您完成大部分工作。


4
请注意,从C++17开始,std::iterator被标记为已弃用 - mandrake

4
在C++中编写自定义迭代器可能会非常冗长和复杂难懂。
由于我找不到一种最简单的编写自定义迭代器的方式,因此我编写了this template header,可以提供帮助。例如,要使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 equal(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容器和迭代器。将每个形状中的点存储在像vector这样的容器中。

class Shape {
    private:
    vector <Point> points;

从那时起,您接下来要做什么取决于您的设计。最好的方法是迭代Shape内部的方法中的点。

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

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

3
他仍然需要创建自己的迭代器,将请求转发到那个形状中的方块。在这里,自定义迭代器是一个很棒的工具,使用起来非常优雅。 - Roel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接