2008-09-29 6 views
132

J'essaie d'apprendre le C++ alors pardonnez-moi si cette question démontre un manque de connaissances de base, vous voyez, le fait est que j'ai un manque de connaissances de base.Créer mes propres itérateurs

Je veux de l'aide pour créer un itérateur pour une classe que j'ai créée.

J'ai une classe 'Forme' qui a un conteneur de Points. J'ai une classe 'Piece' qui fait référence à une forme et définit une position pour la forme. La pièce n'a pas de forme, elle fait simplement référence à une forme.

Je veux que cela ressemble à Piece est un conteneur de points qui sont les mêmes que ceux de la forme qu'il référence mais avec le décalage de la position de la pièce ajoutée. Je veux être capable de parcourir les points de la pièce comme si Piece était un récipient lui-même. J'ai fait un peu de lecture et n'ai rien trouvé qui m'ait aidé. Je serais très reconnaissant pour les pointeurs.

+6

Publier un exemple de code aiderait à décrire ce que vous faites mieux que du simple texte anglais. –

+3

Créer des itérateurs personnalisés est probablement _not_ un début de base, au moins intermédiaire. – ldog

Répondre

41

Vous devez utiliser des Boost.Iterators. Il contient un certain nombre de modèles et de concepts pour implémenter de nouveaux itérateurs et adaptateurs pour les itérateurs existants. J'ai écrit an article about this very topic; C'est dans le magazine de l'ACCU de décembre 2008. Il décrit une solution (IMO) élégante pour exactement votre problème: exposer des collections de membres à partir d'un objet, en utilisant Boost.Iterators.

Si vous souhaitez utiliser uniquement la stl, le Josuttis book comporte un chapitre sur l'implémentation de vos propres itérateurs STL.

+2

Juste une remarque mineure: Le livre parle de la bibliothèque standard C++, pas de la STL - ils sont différents, mais se confondre beaucoup (je suis/était coupable, aussi) – CppChris

59

/EDIT: Je vois, un propre itérateur est réellement nécessaire ici (j'ai mal lu la question d'abord). Pourtant, je laisse le code ci-dessous se tenir, car il peut être utile dans des circonstances similaires.


Un propre itérateur est-il réellement nécessaire ici? Peut-être est suffisant pour transmettre toutes les définitions nécessaires au récipient contenant les points réels:

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

Ceci est en supposant que vous utilisez un interne vector mais le type peut facilement être adapté.

+0

peut-être qu'il veut utiliser l'algorithme STL ou des fonctionnalités fonctionnelles par rapport à sa classe ... – gbjbaanb

+2

La question initiale indique en fait que l'itérateur du conteneur pièce doit modifier les valeurs lors de leur renvoi. Cela nécessiterait un itérateur séparé, bien qu'il devrait probablement être hérité ou obtenu principalement de l'original. – workmad3

+0

@gbjbaanb: La bonne chose à propos de mon code est qu'il * peut * être utilisé par les algorithmes STL. –

1

La solution à votre problème n'est pas la création de vos propres itérateurs, mais l'utilisation de conteneurs STL et d'itérateurs existants. Stocker les points dans chaque forme dans un conteneur comme un vecteur.

class Shape { 
    private: 
    vector <Point> points; 

Ce que vous faites à partir de ce moment dépend de votre conception. La meilleure approche consiste à parcourir les points des méthodes à l'intérieur de Shape.

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

Si vous avez besoin d'accéder à des points de forme à l'extérieur (cela pourrait être une marque d'une conception déficiente), vous pouvez créer des méthodes de forme qui renverront les fonctions d'accès iterator pour les points (dans ce cas aussi créer un typedef publique pour le conteneur de points). Regardez la réponse de Konrad Rudolph pour les détails de cette approche.

+2

Il aura toujours besoin de faire son propre itérateur qui transmet les requêtes à Piece aux formes qui sont dans cette pièce. Les itérateurs personnalisés sont un excellent outil ici, et très élégant à utiliser. – Roel

13

Vous pouvez lire ce ddj article

Fondamentalement, hériter de std :: iterator pour obtenir la plupart du travail accompli pour vous.

19

Ici Designing a STL like Custom Container est un excellent article qui explique certains des concepts de base de la façon dont une classe de conteneur de type STL peut être conçue avec la classe d'itérateur pour cela. iterator inverse (peu plus difficile) mais est laissé comme un exercice :-)

HTH,

0

écriture itérateurs personnalisés en C++ peut être assez bavard et complexe à comprendre.

Depuis que je ne pouvais pas trouver un moyen minimal d'écrire un itérateur personnalisé, j'ai écrit this template header qui pourrait aider. Par exemple, pour faire la Piece classe itérables:

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

Ensuite, vous pourrez l'utiliser comme un conteneur 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; 
} 

Il permet également d'ajouter d'autres types d'itérateurs comme const_iterator ou reverse_const_iterator.

J'espère que ça aide.

Questions connexes