Создание моих собственных итераторов - PullRequest
138 голосов
/ 29 сентября 2008

Я пытаюсь выучить C ++, так что извините, если этот вопрос демонстрирует недостаток базовых знаний, видите, дело в том, что у меня недостаточно базовых знаний.

Мне нужна помощь в разработке итератора для класса, который я создал.

У меня есть класс 'Shape', в котором есть контейнер очков. У меня есть класс Piece, который ссылается на Shape и определяет позицию для Shape. У фигуры нет формы, она просто ссылается на форму.

Я хочу, чтобы казалось, что Пьеса - это контейнер Точек, которые такие же, как и в Форме, на которую он ссылается, но с добавленным смещением положения Пьеса.

Я хочу иметь возможность перебирать точки фигуры так же, как если бы фигура была самим контейнером. Я немного почитал и не нашел ничего, что помогло бы мне. Буду очень признателен за любые указатели.

Ответы [ 6 ]

61 голосов
/ 29 сентября 2008

/ 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 для внутреннего использования, но тип может быть легко адаптирован.

40 голосов
/ 29 сентября 2008

Вы должны использовать Boost.Iterators. Он содержит ряд шаблонов и концепций для реализации новых итераторов и адаптеров для существующих итераторов. Я написал статью на эту тему ; это в журнале ACCU за декабрь 2008 года. В нем обсуждается элегантное решение (IMO) именно для вашей проблемы: предоставление коллекций элементов из объекта с использованием Boost.Iterators.

Если вы хотите использовать только stl, в книге Josuttis есть глава по реализации ваших собственных итераторов STL.

20 голосов
/ 08 мая 2009

Здесь Проектирование STL-подобного пользовательского контейнера - отличная статья, в которой объясняются некоторые основные понятия о том, как можно создать STL-подобный класс-контейнер вместе с классом итератора для него. Обратный итератор (немного сложнее), хотя и оставлен в качестве упражнения: -)

НТН,

15 голосов
/ 29 сентября 2008

Вы можете прочитать эту статью ddj

По сути, наследуйте от std :: iterator, чтобы выполнить большую часть работы за вас.

2 голосов
/ 01 июня 2017

Написание пользовательских итераторов на 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_iterator или reverse_const_iterator.

Надеюсь, это поможет.

1 голос
/ 29 сентября 2008

Решением вашей проблемы является не создание собственных итераторов, а использование существующих контейнеров и итераторов STL. Храните точки в каждой фигуре в контейнере, как вектор.

class Shape {
    private:
    vector <Point> points;

То, что вы делаете с этого момента, зависит от вашего дизайна. Наилучший подход состоит в том, чтобы перебирать точки в методах внутри Shape.

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

Если вам нужно получить доступ к точкам за пределами Shape (это может быть признаком несовершенного дизайна), вы можете создать в Shape методы, которые будут возвращать функции доступа итератора для точек (в этом случае также создать общедоступную typedef для контейнера точек ). Посмотрите на ответ Конрада Рудольфа для деталей этого подхода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...