Tworzenie własnych Iteratorów

141

Próbuję nauczyć się C ++, więc wybacz mi, jeśli to pytanie pokazuje brak podstawowej wiedzy, widzisz, faktem jest, że mam brak podstawowej wiedzy.

Potrzebuję pomocy przy tworzeniu iteratora dla klasy, którą stworzyłem.

Mam klasę „Kształt”, która zawiera zbiór punktów. Mam klasę „Piece”, która odwołuje się do Shape i definiuje pozycję Shape. Kawałek nie ma kształtu, tylko odnosi się do kształtu.

Chcę, żeby wyglądało na to, że Kawałek jest zbiornikiem Punktów, które są takie same jak te z Kształt, do którego się odnosi, ale z dodanym przesunięciem pozycji Kawałka.

Chcę mieć możliwość iteracji punktów elementu tak, jakby Element sam w sobie był pojemnikiem. Poczytałem trochę i nie znalazłem niczego, co mi pomogło. Byłbym bardzo wdzięczny za wszelkie wskazówki.

Howard May
źródło
6
Opublikowanie przykładowego kodu pomogłoby w opisaniu tego, co robisz, lepiej niż zwykły angielski tekst.
Greg Rogers,
3
Tworzenie niestandardowych iteratorów prawdopodobnie nie jest podstawowym szczytem, ​​przynajmniej średnio zaawansowanym.
ldog

Odpowiedzi:

41

Powinieneś użyć Boost.Iterators. Zawiera szereg szablonów i koncepcji do implementacji nowych iteratorów i adapterów dla istniejących iteratorów. Napisałem artykuł na ten temat ; jest w magazynie ACCU z grudnia 2008 roku. Omawia eleganckie rozwiązanie (IMO) dla dokładnie twojego problemu: ujawnianie kolekcji elementów z obiektu przy użyciu Boost.Iterators.

Jeśli chcesz używać tylko stl, w książce Josuttisa znajduje się rozdział poświęcony implementowaniu własnych iteratorów STL.

Roel
źródło
3
Tylko drobna uwaga: książka mówi o bibliotece standardowej C ++, a nie o STL - te są różne, ale często się
gubię
62

/ EDYCJA: Rozumiem, własny iterator jest tutaj właściwie potrzebny (najpierw źle odczytałem pytanie). Mimo wszystko pozostawiam poniższy kod, ponieważ może być przydatny w podobnych okolicznościach.


Czy rzeczywiście potrzebny jest tutaj własny iterator? Być może wystarczy przesłać wszystkie wymagane definicje do kontenera zawierającego rzeczywiste Punkty:

// 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(); }
}

Zakłada się, że używasz vectorwewnętrznie, ale typ można łatwo dostosować.

Konrad Rudolph
źródło
być może chce użyć algorytmu STL lub funkcji funkcjonalnych przeciwko swojej klasie ...
gbjbaanb
2
Pierwotne pytanie faktycznie mówi, że iterator kontenera kawałkowego powinien modyfikować wartości podczas ich zwracania. Wymagałoby to osobnego iteratora, chociaż prawdopodobnie powinien być dziedziczony lub w inny sposób uzyskiwany głównie z oryginału.
workmad3
@gbjbaanb: Zaletą mojego kodu jest to, że może być używany przez algorytmy STL.
Konrad Rudolph
1
Kilka lat później i nadal jest to jeden z najlepszych wyników w Google ... Teraz można to uogólnić, robiąc coś takiego:auto begin() -> decltype(m_shape.container.begin()) { return m_shape.container.begin(); }
user2962533
20

Tutaj Projektowanie STL, takiego jak kontener niestandardowy, jest doskonałym artykułem, który wyjaśnia niektóre podstawowe pojęcia dotyczące tego, jak można zaprojektować klasę kontenera podobną do STL wraz z klasą iteratora. Iterator odwrotny (trochę trudniejszy) pozostaje jednak jako ćwiczenie :-)

HTH,

Abhay
źródło
15

Możesz przeczytać ten artykuł o ddj

Zasadniczo dziedzicz po std :: iterator, aby wykonać większość pracy za Ciebie.

gbjbaanb
źródło
2
Zauważ, że std::iteratorjest oznaczony jako przestarzały z C ++ 17.
mandragora
2

Pisanie niestandardowych iteratorów w C ++ może być dość rozwlekłe i trudne do zrozumienia.

Ponieważ nie mogłem znaleźć minimalnego sposobu na napisanie niestandardowego iteratora, napisałem ten nagłówek szablonu, który może pomóc. Na przykład, aby uczynić Pieceklasę iterowalną:

#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);
};

Wtedy byłbyś w stanie używać go jako zwykłego kontenera 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;
}

Pozwala również na dodawanie innych typów iteratorów, takich jak const_iteratorlub reverse_const_iterator.

Mam nadzieję, że to pomoże.

VinGarcia
źródło
1

Rozwiązaniem Twojego problemu nie jest tworzenie własnych iteratorów, ale wykorzystanie istniejących kontenerów i iteratorów STL. Przechowuj punkty w każdym kształcie w pojemniku, takim jak wektor.

class Shape {
    private:
    vector <Point> points;

Od tego momentu zależy od Twojego projektu. Najlepszym podejściem jest iteracja punktów w metodach wewnątrz Shape.

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

Jeśli potrzebujesz uzyskać dostęp do punktów poza Shape (może to być oznaką wadliwego projektu), możesz utworzyć w Shape metody, które zwrócą funkcje dostępu iteratora dla punktów (w takim przypadku również utwórz publiczny krój pisma dla kontenera punktów). Spójrz na odpowiedź Konrada Rudolpha, aby poznać szczegóły tego podejścia.

Diomidis Spinellis
źródło
3
Nadal będzie musiał stworzyć swój własny iterator, który przekazuje żądania do Elementu do kształtów znajdujących się w tym kawałku. Własne iteratory są tutaj świetnym narzędziem i bardzo eleganckie w użyciu.
Roel