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

141

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

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

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

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

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

Говард Мэй
источник
6
Размещение образца кода поможет описать то, что вы делаете, лучше, чем простой английский текст.
Грег Роджерс,
3
Создание настраиваемых итераторов, вероятно, не является базовой задачей, по крайней мере, промежуточной.
ldog

Ответы:

41

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

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

Roel
источник
3
Небольшое замечание: в книге говорится о стандартной библиотеке C ++, а не о STL - они разные, но сильно
сбивают с
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.
Конрад Рудольф
1
Спустя несколько лет, и это все еще один из лучших результатов в Google ... Теперь можно обобщить это, сделав что-то вроде этого:auto begin() -> decltype(m_shape.container.begin()) { return m_shape.container.begin(); }
user2962533
20

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

HTH,

Абхай
источник
15

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

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

gbjbaanb
источник
2
Обратите внимание, что в C ++ 17 он std::iteratorпомечен как устаревший .
mandrake
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_iteratorили reverse_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 для контейнера точек). Посмотрите ответ Конрада Рудольфа, чтобы узнать подробности этого подхода.

Диомидис Спинеллис
источник
3
Ему по-прежнему нужно будет создать свой собственный итератор, который пересылает запросы к Piece в формы, которые находятся в этом Piece. Пользовательские итераторы - отличный инструмент, и очень элегантный в использовании.
Roel