Створення власних ітераторів


141

Я намагаюся вивчити С ++, тому вибачте мене, якщо це запитання демонструє брак базових знань. Розумієте, факт у мене нестача базових знань.

Я хочу допомогти розробити те, як створити ітератор для створеного мною класу.

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

Я хочу, щоб це здавалося, що Шматок - це контейнер Очок, який такий самий, як і у формі, на яку він посилається, але зі зміщенням позиції П'єси.

Я хочу, щоб я міг перебиратись через очки п'єси так само, як ніби Piece був самим контейнером. Я трохи почитав і не знайшов нічого, що допомогло б мені. Я був би дуже вдячний за будь-які покажчики.


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

3
Створення користувальницьких ітераторів, мабуть, не є базовим верхом, принаймні проміжним.
ldog

Відповіді:


41

Вам слід використовувати Boost.Iterators. Він містить низку шаблонів та концепцій для впровадження нових ітераторів та адаптерів для існуючих ітераторів. Я написав статтю на цю саму тему ; це у журналі ACCU за грудень 2008 року. У ньому обговорюється (IMO) елегантне рішення саме для вашої проблеми: викриття колекцій членів від об'єкта за допомогою Boost.Iterators.

Якщо ви хочете використовувати лише stl, книга Josuttis має розділ про реалізацію власних ітераторів STL.


3
Лише незначне зауваження: Книга розповідає про стандартну бібліотеку C ++, а не про STL - вони різні, але сильно плутайтесь (я теж / винен)
CppChris

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, як спеціальний контейнер, є чудовою статтею, в якій пояснюються деякі основні поняття того, як може бути спроектований клас контейнерів типу 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 to Shapes, що знаходяться в цьому Piece. Спеціальні ітератори тут - чудовий інструмент та дуже елегантний у використанні.
Roel
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.