J'ai créé une collection pour laquelle je veux fournir un itérateur à accès aléatoire de style STL. Je cherchais un exemple d'implémentation d'un itérateur mais je n'en ai trouvé aucun. Je connais le besoin de surcharges const []
et d' *
opérateurs. Quelles sont les exigences pour qu'un itérateur soit de "style STL" et quels sont les autres pièges à éviter (le cas échéant)?
Contexte supplémentaire: c'est pour une bibliothèque et je ne veux pas en dépendre à moins d'en avoir vraiment besoin. J'écris ma propre collection pour pouvoir fournir une compatibilité binaire entre C ++ 03 et C ++ 11 avec le même compilateur (donc pas de STL qui casserait probablement).
c++
iterator
const-iterator
Tamás Szelei
la source
la source
Réponses:
http://www.cplusplus.com/reference/std/iterator/ a un tableau pratique qui détaille les spécifications du § 24.2.2 de la norme C ++ 11. Fondamentalement, les itérateurs ont des balises qui décrivent les opérations valides et les balises ont une hiérarchie. Ci-dessous est purement symbolique, ces classes n'existent pas réellement en tant que telles.
Vous pouvez soit vous spécialiser
std::iterator_traits<youriterator>
, soit mettre les mêmes typedefs dans l'itérateur lui-même, soit hériter destd::iterator
(qui a ces typedefs). Je préfère la deuxième option, pour éviter de changer les choses dans l'std
espace de noms et pour la lisibilité, mais la plupart des gens en héritentstd::iterator
.Notez le iterator_category devrait être l' un
std::input_iterator_tag
,std::output_iterator_tag
,std::forward_iterator_tag
,std::bidirectional_iterator_tag
oustd::random_access_iterator_tag
, selon laquelle les exigences itérateur satisfait. En fonction de votre iterator, vous pouvez choisir de se spécialiserstd::next
,std::prev
,std::advance
etstd::distance
aussi bien, mais cela est rarement nécessaire. Dans des cas extrêmement rares , vous souhaiterez peut-être vous spécialiserstd::begin
etstd::end
.Votre conteneur devrait probablement également avoir un
const_iterator
, qui est un itérateur (éventuellement modifiable) pour des données constantes similaire à la vôtre,iterator
sauf qu'il devrait être implicitement constructible à partir de aiterator
et les utilisateurs ne devraient pas être en mesure de modifier les données. Il est courant que son pointeur interne soit un pointeur vers des données non constantes et eniterator
hériteconst_iterator
afin de minimiser la duplication de code.Mon message à Writing your own STL Container a un prototype de conteneur / itérateur plus complet.
la source
std::iterator_traits
ou de définir les typedefs vous-même, vous pouvez également en dériverstd::iterator
, qui les définit pour vous, en fonction de ses paramètres de modèle.const_iterator
. Que manquait-il d'autre à mon message? Vous semblez impliquer qu'il y a plus à ajouter à la classe, mais la question concerne spécifiquement l'implémentation des itérateurs.std::iterator
a été proposé d'être déconseillé en C ++ 17 ; ce n'était pas le cas, mais je ne m'attendrais pas à ce que ça dure plus longtemps.std::iterator
été dépréciée après tout.operator bool
incroyablement dangereux. Quelqu'un essaiera de l'utiliser pour détecter la fin d'une plagewhile(it++)
, mais tout ce qu'il vérifie vraiment, c'est si l'itérateur a été construit avec un paramètre.La documentation iterator_facade de Boost.Iterator fournit ce qui ressemble à un joli didacticiel sur l'implémentation d'itérateurs pour une liste chaînée. Pourriez-vous l'utiliser comme point de départ pour créer un itérateur à accès aléatoire sur votre conteneur?
Si rien d'autre, vous pouvez jeter un œil aux fonctions membres et aux typedefs fournis par
iterator_facade
et les utiliser comme point de départ pour créer les vôtres.la source
Thomas Becker a écrit un article utile sur le sujet ici .
Il y avait aussi cette approche (peut-être plus simple) qui était apparue précédemment sur SO: Comment implémenter correctement les itérateurs et const_iterators personnalisés?
la source
Voici un exemple d'itérateur de pointeur brut.
Vous ne devez pas utiliser la classe itérateur pour travailler avec des pointeurs bruts!
Solution de boucle basée sur une plage de pointeurs bruts. S'il vous plaît, corrigez-moi, s'il existe un meilleur moyen de créer une boucle basée sur une plage à partir d'un pointeur brut.
Et test simple
la source
Tout d'abord, vous pouvez consulter ici une liste des différentes opérations que les types d'itérateurs individuels doivent prendre en charge.
Ensuite, lorsque vous avez créé votre classe d'itérateur, vous devez soit vous spécialiser
std::iterator_traits
et fournir certainstypedef
s nécessaires (commeiterator_category
ouvalue_type
), soit en dériverstd::iterator
, ce qui définit lestypedef
s nécessaires pour vous et peut donc être utilisé avec la valeur par défaut.std::iterator_traits
.Avertissement: Je sais que certaines personnes n'aiment pas
cplusplus.com
beaucoup cela, mais elles fournissent des informations très utiles à ce sujet.la source
J'étais / suis dans le même bateau que vous pour différentes raisons (en partie éducatives, en partie contraintes). J'ai dû réécrire tous les conteneurs de la bibliothèque standard et les conteneurs devaient être conformes à la norme. Cela signifie que si j'échange mon conteneur avec la version stl , le code fonctionnera de la même manière. Ce qui signifiait également que je devais réécrire les itérateurs.
Quoi qu'il en soit, j'ai regardé EASTL . En plus d'apprendre une tonne sur les conteneurs que je n'ai jamais appris tout ce temps en utilisant les conteneurs stl ou à travers mes cours de premier cycle. La raison principale est que EASTL est plus lisible que l' homologue stl (j'ai trouvé que c'était simplement à cause du manque de toutes les macros et du style de codage simple). Il y a des choses épineuses là-dedans (comme #ifdefs pour les exceptions) mais rien pour vous submerger.
Comme d'autres l'ont mentionné, consultez la référence de cplusplus.com sur les itérateurs et les conteneurs.
la source
J'essayais de résoudre le problème de pouvoir itérer sur plusieurs tableaux de texte différents, tous stockés dans une base de données résidente en mémoire qui est grande
struct
.Les éléments suivants ont été élaborés à l'aide de Visual Studio 2017 Community Edition sur une application de test MFC. J'inclus ceci à titre d'exemple, car cette publication était l'une des nombreuses que j'ai rencontrées et qui fournissaient de l'aide mais qui étaient encore insuffisantes pour mes besoins.
Le
struct
contenant les données résidentes en mémoire ressemblait à quelque chose comme suit. J'ai supprimé la plupart des éléments par souci de concision et n'ai pas non plus inclus les définitions de préprocesseur utilisées (le SDK utilisé est pour C ainsi que C ++ et est ancien).Ce qui m'intéressait, c'était d'avoir des itérateurs pour les différents
WCHAR
tableaux bidimensionnels qui contenaient des chaînes de texte pour les mnémoniques.L'approche actuelle consiste à utiliser un modèle pour définir une classe proxy pour chacun des tableaux, puis à avoir une seule classe d'itérateur qui peut être utilisée pour itérer sur un tableau particulier en utilisant un objet proxy représentant le tableau.
Une copie des données résidentes en mémoire est stockée dans un objet qui gère la lecture et l'écriture des données résidentes en mémoire depuis / vers le disque. Cette classe,
CFilePara
contient la classe proxy sur matrice (MnemonicIteratorDimSize
et la sous - classe à partir de laquelle elle est dérivée est,MnemonicIteratorDimSizeBase
) et la classe d'itération,MnemonicIterator
.L'objet proxy créé est attaché à un objet itérateur qui accède aux informations nécessaires via une interface décrite par une classe de base à partir de laquelle toutes les classes proxy sont dérivées. Le résultat est d'avoir un seul type de classe d'itérateur qui peut être utilisé avec plusieurs classes proxy différentes car les différentes classes proxy exposent toutes la même interface, l'interface de la classe de base proxy.
La première chose a été de créer un ensemble d'identifiants qui seraient fournis à une fabrique de classes pour générer l'objet proxy spécifique pour ce type de mnémonique. Ces identifiants sont utilisés dans le cadre de l'interface utilisateur pour identifier les données d'approvisionnement particulières que l'utilisateur souhaite voir et éventuellement modifier.
La classe proxy
La classe proxy basée sur un modèle et sa classe de base sont les suivantes. J'avais besoin de prendre en charge plusieurs types de
wchar_t
tableaux de chaînes de texte. Les tableaux bidimensionnels avaient différents nombres de mnémoniques, selon le type (but) de la mnémonique et les différents types de mnémoniques étaient de longueurs maximales différentes, variant entre cinq caractères de texte et vingt caractères de texte. Les modèles de la classe proxy dérivée correspondaient naturellement au modèle nécessitant le nombre maximal de caractères dans chaque mnémonique. Une fois l'objet proxy créé, nous utilisons ensuite laSetRange()
méthode pour spécifier le tableau mnémonique réel et sa plage.La classe des itérateurs
La classe d'itérateur elle-même est la suivante. Cette classe fournit simplement une fonctionnalité d'itérateur direct de base, ce qui est tout ce qui est nécessaire pour le moment. Cependant, je m'attends à ce que cela change ou soit étendu lorsque j'en ai besoin de quelque chose de plus.
La fabrique d'objets proxy détermine quel objet à créer en fonction de l'identifiant mnémonique. L'objet proxy est créé et le pointeur renvoyé est le type de classe de base standard afin d'avoir une interface uniforme quelle que soit la section mnémonique à laquelle on accède. La
SetRange()
méthode est utilisée pour spécifier à l'objet proxy les éléments de tableau spécifiques que le proxy représente et la plage des éléments de tableau.Utilisation de la classe proxy et de l'itérateur
La classe proxy et son itérateur sont utilisés comme indiqué dans la boucle suivante pour remplir un
CListCtrl
objet avec une liste de mnémoniques. J'utilise destd::unique_ptr
sorte que lorsque la classe proxy n'est plus nécessaire et qu'ellestd::unique_ptr
sort du cadre, la mémoire sera nettoyée.Ce que fait ce code source, c'est de créer un objet proxy pour le tableau dans
struct
lequel correspond à l'identifiant mnémonique spécifié. Il crée ensuite un itérateur pour cet objet, utilise une distancefor
pour remplir leCListCtrl
contrôle, puis nettoie. Ce sont toutes deswchar_t
chaînes de texte brutes qui peuvent être exactement le nombre d'éléments de tableau, nous copions donc la chaîne dans un tampon temporaire afin de nous assurer que le texte se termine par zéro.la source
Et maintenant un itérateur de clés pour la boucle basée sur la plage.
Usage:
Voilà ce que je cherchais. Mais personne ne l'avait, semble-t-il.
Vous obtenez mon alignement de code OCD en bonus.
Comme exercice, écrivez le vôtre
values(my_map)
la source