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


141

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

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

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

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

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


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

3
Создание настраиваемых итераторов, вероятно, не является базовой задачей, по крайней мере, промежуточной.
ldog

Ответы:


41

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

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


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,



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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.