Создание моих собственных итераторов
Я пытаюсь выучить 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 для контейнера точек). Посмотрите на ответ Конрада Рудольфа для деталей этого подхода.