Creando mis propios iteradores

141

Estoy tratando de aprender C ++, así que perdóname si esta pregunta demuestra una falta de conocimiento básico, ya ves, el hecho es que tengo una falta de conocimiento básico.

Quiero ayuda para resolver cómo crear un iterador para una clase que he creado.

Tengo una clase 'Forma' que tiene un contenedor de Puntos. Tengo una clase 'Pieza' que hace referencia a una Forma y define una posición para la Forma. La pieza no tiene una forma, solo hace referencia a una forma.

Quiero que parezca que Piece es un contenedor de puntos que son los mismos que los de la forma a la que hace referencia, pero con el desplazamiento de la posición de Piece agregada.

Quiero poder recorrer los puntos de Piece como si Piece fuera un contenedor. He leído un poco y no he encontrado nada que me haya ayudado. Estaría muy agradecido por cualquier sugerencia.

Howard May
fuente
66
Publicar código de muestra ayudaría a describir lo que está haciendo mejor que solo texto en inglés.
Greg Rogers
3
La creación de iteradores personalizados probablemente no sea una parte superior básica, al menos intermedia.
ldog

Respuestas:

41

Deberías usar Boost.Iterators. Contiene una serie de plantillas y conceptos para implementar nuevos iteradores y adaptadores para los iteradores existentes. He escrito un artículo sobre este mismo tema ; está en la revista ACCU de diciembre de 2008. Discute una solución elegante (IMO) para exactamente su problema: exponer colecciones de miembros de un objeto, usando Boost.Iterators.

Si solo quiere usar el stl, el libro de Josuttis tiene un capítulo sobre la implementación de sus propios iteradores STL.

Roel
fuente
3
Solo un pequeño comentario: el libro habla sobre la Biblioteca estándar de C ++, no sobre el STL: estos son diferentes, pero se confunden mucho (también soy / fui culpable)
CppChris
62

/ EDITAR: Ya veo, un iterador propio es realmente necesario aquí (primero leí mal la pregunta). Aún así, estoy dejando que el código a continuación se mantenga porque puede ser útil en circunstancias similares.


¿Es realmente necesario un iterador propio aquí? Quizás sea suficiente enviar todas las definiciones requeridas al contenedor que contiene los puntos reales:

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

Esto supone que está utilizando un vectorinterno, pero el tipo se puede adaptar fácilmente.

Konrad Rudolph
fuente
tal vez él quiere usar el algoritmo de STL o características funcionales en contra de su clase ...
gbjbaanb
2
La pregunta original en realidad dice que el iterador del contenedor de piezas debe modificar los valores al devolverlos. Eso requeriría un iterador separado, aunque probablemente debería heredarse u obtenerse principalmente del original.
workmad3
@gbjbaanb: Lo bueno de mi código es que puede ser utilizado por algoritmos STL.
Konrad Rudolph
1
Algunos años más tarde y este todavía se encuentra entre los mejores resultados en Google ... Ahora es posible generalizar esto haciendo algo como esto:auto begin() -> decltype(m_shape.container.begin()) { return m_shape.container.begin(); }
user2962533
20

Aquí, Diseñar un STL como Custom Container es un excelente artículo que explica algunos de los conceptos básicos de cómo se puede diseñar una clase de STL como contenedor junto con la clase de iterador. Sin embargo, el iterador inverso (un poco más duro) se deja como ejercicio :-)

HTH

Abhay
fuente
15

Puedes leer este artículo de ddj

Básicamente, herede de std :: iterator para hacer la mayor parte del trabajo por usted.

gbjbaanb
fuente
2
Tenga en cuenta que std::iteratorestá marcado como obsoleto de C ++ 17.
mandrake el
2

Escribir iteradores personalizados en C ++ puede ser bastante detallado y complejo de entender.

Como no pude encontrar una forma mínima de escribir un iterador personalizado, escribí este encabezado de plantilla que podría ayudar. Por ejemplo, para hacer que la Piececlase sea iterable:

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

Entonces podrá usarlo como un contenedor STL normal:

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

También permite agregar otros tipos de iteradores como const_iteratoro reverse_const_iterator.

Espero que ayude.

VinGarcia
fuente
1

La solución a su problema no es la creación de sus propios iteradores, sino el uso de contenedores e iteradores STL existentes. Almacene los puntos en cada forma en un contenedor como vector.

class Shape {
    private:
    vector <Point> points;

Lo que hagas a partir de entonces depende de tu diseño. El mejor enfoque es iterar a través de puntos en métodos dentro de Shape.

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

Si necesita acceder a puntos fuera de Shape (esto podría ser una marca de un diseño deficiente), puede crear en los métodos Shape que devolverán las funciones de acceso del iterador para los puntos (en ese caso, también creará una definición pública de tipo para el contenedor de puntos). Mire la respuesta de Konrad Rudolph para obtener detalles de este enfoque.

Diomidis Spinellis
fuente
3
Todavía necesitará hacer su propio iterador que reenvíe las solicitudes a Piece a las Formas que están en esa Piece. Los iteradores personalizados son una gran herramienta aquí, y muy elegantes de usar.
Roel