Создание моих собственных итераторов

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

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

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

Я хочу, чтобы это выглядело так, как будто Piece - это контейнер Точек, которые совпадают с теми из Shape, на которые он ссылается, но с добавленным смещением позиции Piece.

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

6 ответов

Решение

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

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

/ РЕДАКТИРОВАТЬ: я вижу, здесь нужен собственный итератор (сначала я неправильно прочитал вопрос). Тем не менее, я оставляю код ниже, потому что он может быть полезен в подобных обстоятельствах.


Нужен ли здесь собственный итератор? Возможно, достаточно переслать все необходимые определения в контейнер, содержащий фактические Баллы:

// 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-подобного пользовательского контейнера" - отличная статья, в которой объясняются некоторые основные понятия о том, как можно создать STL-подобный класс-контейнер вместе с классом итератора для него. Обратный итератор (немного сложнее), хотя и оставлен в качестве упражнения:-)

НТН,

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

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

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

Я надеюсь, что это помогает.

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

class Shape {
    private:
    vector <Point> points;

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

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

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

Другие вопросы по тегам