web-dev-qa-db-fra.com

Créer mes propres itérateurs

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

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

J'ai une classe 'Shape' 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 paraisse comme si Piece était un conteneur de points identiques à ceux de la forme à laquelle il fait référence, mais avec le décalage de la position de la pièce ajouté.

Je veux pouvoir parcourir les points de Piece comme si Piece était lui-même un conteneur. J'ai fait un peu de lecture et n'ai rien trouvé qui puisse m'aider. Je serais très reconnaissant pour tous les pointeurs.

139
Howard May

Vous devriez utiliser Boost.Iterators. Il contient un certain nombre de modèles et de concepts permettant d'implémenter de nouveaux itérateurs et adaptateurs pour les itérateurs existants. J'ai écrit n article sur ce sujet ; c'est dans le magazine ACCU de décembre 2008. Il aborde une solution élégante (IMO) pour votre problème: exposer les collections de membres d'un objet à l'aide de Boost.Iterators.

Si vous voulez utiliser uniquement stl, le livre de Josuttis contient un chapitre sur la mise en oeuvre de vos propres itérateurs STL.

40
Roel

/ EDIT: Je vois qu’un propre itérateur est en fait nécessaire ici (j’ai mal interprété la question en premier). Néanmoins, je laisse le code ci-dessous rester inchangé, car il peut être utile dans des circonstances similaires.


Un propre itérateur est-il réellement nécessaire ici? Peut-être suffit-il de transmettre toutes les définitions requises au conteneur 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(); }
}

Cela suppose que vous utilisez vector en interne, mais que le type peut facilement être adapté.

61
Konrad Rudolph

Here Conception d'un STL comme un conteneur personnalisé est un excellent article qui explique certains des concepts de base de la manière dont une classe de conteneur de type STL peut être conçue avec la classe itérateur correspondante. Itérateur inversé (un peu plus difficile) reste cependant comme exercice :-)

HTH,

20
Abhay

Vous pouvez lire ceci article de ddj

En gros, héritez de std :: iterator pour faire le gros du travail à votre place.

15
gbjbaanb

Écrire des itérateurs personnalisés en C++ peut être assez détaillé et complexe à comprendre.

Comme je ne pouvais pas trouver un moyen minimal d’écrire un itérateur personnalisé, j’écrivis cet en-tête de modèle cela pourrait aider. Par exemple, pour rendre la classe Piece itérable:

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

Cela permet également d’ajouter d’autres types d’itérateurs comme const_iterator ou reverse_const_iterator.

J'espère que ça aide.

2
VinGarcia

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

class Shape {
    private:
    vector <Point> points;

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

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

Si vous devez accéder à des points extérieurs à Shape (cela peut être le signe d'une conception déficiente), vous pouvez créer des méthodes dans Shape qui renverront les fonctions d'accès itérateur pour les points (dans ce cas, créez également une typedef publique pour le conteneur de points). Regardez la réponse de Konrad Rudolph pour plus de détails sur cette approche.

1
Diomidis Spinellis