2008-09-29 23 views
132

Estoy tratando de aprender C++, así que discúlpeme si esta pregunta demuestra una falta de conocimiento básico, ya ve, el hecho es que tengo una falta de conocimiento básico.Creando mis propios iteradores

Quiero un poco de ayuda para averiguar cómo crear un iterador para una clase que he creado.

Tengo una clase 'Shape' 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 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 la pieza añadida.

Quiero poder iterar a través de los puntos de la pieza como si la pieza fuera un contenedor. He leído un poco y no he encontrado nada que me haya ayudado. Estaría muy agradecido por cualquier punteros.

+6

Publicar el código de muestra ayudaría a describir lo que está haciendo mejor que simplemente texto en inglés. –

+3

La creación de iteradores personalizados es probablemente _no_ una parte superior básica, intermedia al menos. – ldog

Respuesta

41

Debe usar Boost.Iterators. Contiene una serie de plantillas y conceptos para implementar nuevos iteradores y adaptadores para iteradores existentes. He escrito an article about this very topic; está en la revista ACCU de diciembre de 2008. Discute una solución elegante (IMO) para su problema exactamente: exponer colecciones de miembros de un objeto, usando Boost.Iterators.

Si solo desea utilizar stl, el Josuttis book tiene un capítulo sobre cómo implementar sus propios iteradores STL.

+2

Solo una pequeña observación: el libro habla sobre la Biblioteca Estándar de C++, no sobre el STL; estos son diferentes, pero se confunden mucho (yo también soy/soy culpable) – CppChris

59

/EDITAR: Veo, un iterador propio es realmente necesario aquí (he leído mal la pregunta primero). Aún así, dejo que el código a continuación permanezca porque puede ser útil en circunstancias similares.


¿Es realmente necesario un iterador propio aquí? Tal vez sea suficiente para desviar todas las definiciones que se requieren para el recipiente 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 es suponiendo que estés usando un vector internamente, pero el tipo se puede adaptar fácilmente.

+0

quizás quiera usar el algoritmo STL o las características funcionales en su clase ... – gbjbaanb

+2

La pregunta original en realidad dice que el iterador del contenedor de la pieza debe modificar los valores al devolverlos. Eso requeriría un iterador separado, aunque probablemente debería ser heredado u obtenido en su mayoría del original. – workmad3

+0

@gbjbaanb: Lo bueno de mi código es que * puede * ser utilizado por algoritmos STL. –

1

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

class Shape { 
    private: 
    vector <Point> points; 

Lo que haga a partir de entonces dependerá de su diseño. El mejor enfoque es repetir los puntos en los métodos dentro de Shape.

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

Si necesita acceder a puntos de la forma exterior (esto podría ser una señal de un diseño deficiente) puede crear en los métodos de la forma que devolverán las funciones de acceso iterador de puntos (en ese caso también crear un typedef público para el contenedor de puntos). Mire la respuesta de Konrad Rudolph para detalles de este enfoque.

+2

Todavía necesitará crear su propio iterador que envíe las solicitudes a Piece a las Formas que están en esa Pieza. Los iteradores personalizados son una gran herramienta aquí y muy elegantes de usar. – Roel

13

Puede leer este ddj article

Básicamente, heredar de std :: iterador para conseguir mayor parte del trabajo hecho por ti.

19

Aquí Designing a STL like Custom Container es un excelente artículo que explica algunos de los conceptos básicos de cómo una clase de contenedor tipo STL se puede diseñar junto con la clase de iterador para ella. iterador inverso (poco más difícil), aunque se deja como un ejercicio :-)

HTH,

0

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

Como no pude encontrar una forma mínima de escribir un iterador personalizado, escribí this template header que podría ser útil. Por ejemplo, para hacer que el iterable Piece clase:

#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 sería capaz de utilizarlo como un contenedor normal de 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; 
} 

También permite la adición de otros tipos de iteradores como const_iterator o reverse_const_iterator.

Espero que ayude.

Cuestiones relacionadas