Salut,
De manière générale, un itérateur est un concept dont le but est de permettre ... d'itérer sur les différents éléments d'une collection sans avoir à s'inquiéter de la manière dont les éléments sont maintenus en mémoire au sein de cette collection.
Quand on décide d'utiliser ce concept, on sait que l'on peut accéder au premier élément d'une collection quelconque à l'aide d'une fonction membre nommée begin() et on sait que la fonction end() permet d'accéder à ... ce qui suit le dernier élément valide contenu par cette même collection.
En 2011, un certain nombre de fonctionnalités sympa sont apparues, telles que:
1- L'inférence de type, qui permet de déterminer le type d'une variable sur base du type de retour de la fonction à laquelle on fait appel. C'est cool, parce que, si on a un tableau de chaines de caractères -- par exemple -- qui prend la forme de
std::vector<std::string> tab;
nous ne sommes plus obligés d'indiquer explicitement le type réel de l'information renvoyée par la fonciton begin, ce qui aurait pris la forme de
std::vector<std::string>::iterator it = tab.begin();
parce que c'est long et fastidieux.
Et encore, j'ai mis la version courte, car std::string n'est qu'un alias de type sur std::basic_string<char, std::char_trait<char>, std::alocator<char>>. Je te laisse donc imaginer à quoi ressemblerait la version longue
Désormais, grâce à l'inférence de type (et au mot clé auto), on peut se contenter d'un code qui prenne la forme de
auto it = tab.begin(); //it est de type "std::vector<std::string>::iterator
Il faut comprendre que l'inférence de type n'a rien supprimé du tout, pas plus qu'elle n'a remplacé quoi que ce soit : toutes les fonctionnalités qui existaient avant C++11 existent encore en C++11, compatibilité avec le code existant oblige.
L'inférence de type a juste apporté "une nouvelle possibilité" qui est
- plus simple : un seul mot peut être utilisé pour désigner un type de donnée "à charnières et à rallonges"
- plus souple : si je décide de remplacer le std::vector par un std::list ou par un std::set, je ne dois pas commencer à chercher tous les endroits dans le code où j'utilise un itérateur (et où je le défini comme étant le résultat renvoyé par la fonction begin()) afin d'en corriger le type
- plus "élégante", d'un certain point de vue
En un mot comme en cent, ce n'est pas parce que l'on utilise auto que l'on n'utilise plus les itérateurs : on décide "simplement" de s'offrir la possibilité d'être "moins attentif" à leur type réel.
2- les range based loops (ou les boucle "basée sur des intervalles).
Le comité s'est rendu compte que la manière "classique" (je devrais dire "historique") de parcourir les éléments d'une collection n'était pas "la plus facile qui soit", car elle est basée sur la syntaxe "historique" permettant de définir une boucle "pour", à savoir
for( <type> <nom> = <valeur de départ>; <condition de sortie>; <passage à la valeur suivante>). Ce qui est assez lourd à écrire lorsqu'il s'agit de parcourir tous les éléments d'une collection dynamique.
Et il a trouvé cela bien dommage, car, il s'est également rendu compte que toutes les collections proposées par la bibliothèque standard exposent les fonctions begin et end (qui permettent toutes les deux d'obtenir un itérateur sur le contenu de la collection), et parce qu'une grosse majorité des boucles que l'on est amené à créer dans les différentes situations qui impliquent des collections sont destinées à... parcourir l'ensemble des éléments que ces collections contiennent.
L'un dans l'autre, certains langages avaient déjà donné le ton en permettant la création de boucles dont la signification est "pour chaque élément contenu par ma collection". Ce genre de boucle ne nécessite que deux éléments syntaxiques (contre trois pour les boucles "classiques"), à savoir:
- un identifiant représentant la donnée qui contiendra chacun des éléments et
- l'identifiant qui sert de collection
étant entendu que ce nouveau type de boucle devra systématiquement parcourir tous les éléments que l'on peut croiser entre l'élément renvoyé par la fonction begin() et celui entre l'élément renvoyé par la fonction end().
Encore une fois, les boucles basées sur les intervalles n'ont occasionné ni suppression ni remplacement au sein du langage et / ou de la bibliothèque standard : c'est une nouvelle possibilité qui utilise ingénieusement la présence d'éléments connus pour nous faciliter la vie en rendant l'usage de ces éléments implicites.
Comme l'a si bien fait remarquer Btkaro, les boucles basées sur les intervalles ne font rien d'autre que de mettre en place une boucle "pour" qui s'appuie implicitement sur les fonctions begin et end. La seule différence, c'est qu'on n'est plus obligé d'y avoir recours de manière explicite
3- d'autres joyeusetés, comme les pointeurs intelligents, la notion d'ownership, la sémantique de déplacement, les threads, les fonctionnalité de gestion fine du temps et d'aléatoire, et bien d'autres encore.
Au final, il faut juste que tu te rende compte que ce n'est pas parce que tu ne semble plus avoir recours à "quelque chose" -- surtout si l'on parle d'une notion aussi "basique" et "d"usage général" que la notion d'itérateurs -- que tu n'a plus besoin de connaître cette notion:
La connaissance de cette notion sert toujours de "mortier" permettant de relier "un certain nombre" (en augmentation, qui plus est) de possibilités entre elles, même si les nouvelles possibilités nous permettent d'être "moins attentif", de les utiliser presque "sans avoir à nous en rendre compte"
Partager