Itérer à travers un vecteur C ++ en utilisant une boucle 'for'

140

Je suis nouveau dans le langage C ++. J'ai commencé à utiliser des vecteurs, et j'ai remarqué que dans tout le code que je vois pour itérer à travers un vecteur via des indices, le premier paramètre de la forboucle est toujours quelque chose basé sur le vecteur. En Java, je pourrais faire quelque chose comme ça avec une ArrayList:

for(int i=0; i < vector.size(); i++){
   vector[i].doSomething();
}

Y a-t-il une raison pour laquelle je ne vois pas cela en C ++? Est-ce une mauvaise pratique?

Flynn
la source
1
La boucle for n'est pas une fonction, donc elle n'a pas de paramètres (ou d'arguments, ce que vous passez). Voulez-vous dire quelque chose comme std::vector<int>::size_type i = 0;, cependant, ou peut std::vector<int>::iterator it = vector.begin();- être ?
chris
Exactement, tous les exemples que je vois sont écrits comme ça.
Flynn
4
En Java, je préférerais une boucle for-each ou utiliser des itérateurs. À peu près identique à C ++ bien que la syntaxe soit légèrement différente.
Jesse Good
10
La plupart des réponses ici supposent à tort que le Q est: quel est le meilleur / le plus court moyen d'itérer std::vector? , le Q réel demandé ici est: Y a - t-il une raison pour laquelle je ne vois pas ceci en C ++? Est-ce une mauvaise pratique? aka Pourquoi est-ce que je vois toujours du code en C ++ qui utilise des itérateurs lors de l'itération std::vector?
Alok Enregistrer

Réponses:

93

Y a-t-il une raison pour laquelle je ne vois pas cela en C ++? Est-ce une mauvaise pratique?

Non. Ce n'est pas une mauvaise pratique, mais l'approche suivante rend votre code une certaine flexibilité .

Habituellement, pré-C ++ 11, le code d'itération sur les éléments de conteneur utilise des itérateurs, quelque chose comme:

std::vector<int>::iterator it = vector.begin();

C'est parce que cela rend le code plus flexible.

Tous les conteneurs de bibliothèque standard prennent en charge et fournissent des itérateurs. Si, à un stade ultérieur du développement, vous devez basculer vers un autre conteneur, ce code n'a pas besoin d'être modifié.

Remarque: L'écriture de code qui fonctionne avec tous les conteneurs de bibliothèques standard possibles n'est pas aussi facile que cela puisse paraître.

Alok Save
la source
25
Quelqu'un pourrait-il m'expliquer pourquoi, dans ce cas particulier / extrait de code, vous conseillez les itérateurs sur l'indexation? De quelle «flexibilité» parlez-vous? Personnellement, je n'aime pas les itérateurs, ils gonflent le code - simplement plus de caractères à taper pour le même effet. Surtout si vous ne pouvez pas utiliser auto.
Violet Giraffe
8
@VioletGiraffe: Lors de l'utilisation d'itérateurs, il est difficile de se tromper avec certains cas comme les plages vides et le code est plus verbeux. Bien sûr, c'est une question ou une perception et un choix, donc cela peut être débattu sans fin.
Alok Enregistrer
9
Pourquoi ne montrez-vous que comment déclarer l'itérateur mais pas comment l'utiliser pour faire la boucle ...?
underscore_d
117

La raison pour laquelle vous ne voyez pas une telle pratique est assez subjective et ne peut pas avoir de réponse définitive, car j'ai vu de nombreux codes qui utilisent la méthode que vous avez mentionnée plutôt que le iteratorcode de style.

Voici quelques raisons pour lesquelles les gens ne considèrent pas la vector.size()manière de boucler:

  1. Être paranoïaque à propos de l'appel à size()chaque fois dans la condition de boucle. Cependant, soit c'est un non-problème, soit cela peut être résolu de manière triviale
  2. Préférer std::for_each()à la forboucle elle-même
  3. Changement ultérieur du conteneur std::vectorà un autre (par exemple map,list ) , il faudra aussi le changement du mécanisme en boucle, car pas tout le soutien récipient size()style de boucle

C ++ 11 fournit une bonne facilité pour se déplacer dans les conteneurs. Cela s'appelle «boucle basée sur la plage pour» (ou «boucle for améliorée» en Java).

Avec peu de code, vous pouvez parcourir le plein (obligatoire!) std::vector:

vector<int> vi;
...
for(int i : vi) 
  cout << "i = " << i << endl;
iammilind
la source
12
Juste pour noter un petit inconvénient du range based for loop : vous ne pouvez pas l'utiliser avec #pragma omp parallel for.
liborm
2
J'aime la version compacte car il y a moins de code à lire. Une fois que vous avez effectué l'ajustement mental, c'est beaucoup plus facile à comprendre et les bogues ressortent davantage. Cela rend également les choses beaucoup plus évidentes lorsqu'une itération non standard se produit car il y a un morceau de code beaucoup plus important.
Code Abominator
87

La manière la plus propre d'itérer à travers un vecteur est via les itérateurs:

for (auto it = begin (vector); it != end (vector); ++it) {
    it->doSomething ();
}

ou (équivalent à ce qui précède)

for (auto & element : vector) {
    element.doSomething ();
}

Avant C ++ 0x, vous devez remplacer auto par le type d'itérateur et utiliser des fonctions membres au lieu des fonctions globales de début et de fin.

C'est probablement ce que vous avez vu. Par rapport à l'approche que vous évoquez, l'avantage est que vous ne dépendez pas beaucoup du type de vector. Si vous passez vectorà une autre classe de «type collection», votre code fonctionnera probablement toujours. Cependant, vous pouvez également faire quelque chose de similaire en Java. Il n'y a pas beaucoup de différence conceptuellement; C ++, cependant, utilise des modèles pour implémenter cela (par rapport aux génériques en Java); par conséquent, l'approche fonctionnera pour tous les types pour lesquels beginet des endfonctions sont définies, même pour les types non-classe tels que les tableaux statiques. Voir ici: Comment fonctionne la fonction basée sur la plage pour les tableaux simples?

JohnB
la source
5
auto, free begin / end sont également C ++ 11. Et aussi, vous devriez l'utiliser ++, au lieu de ++ dans de nombreux cas.
ForEveR
Oui tu as raison. La mise en œuvre beginet end, cependant, est une ligne unique.
JohnB
@JohnB c'est plus d'une ligne, car cela fonctionne aussi pour les tableaux de taille fixe. autod'un autre côté, ce serait assez délicat.
juanchopanza
Si vous n'en avez besoin que pour le vecteur, c'est une ligne unique.
JohnB
Néanmoins, le premier exemple est trompeur, car il ne peut pas fonctionner en C ++ 03, alors que votre formulation le suggère.
juanchopanza
35

La bonne façon de faire est de:

for(std::vector<T>::iterator it = v.begin(); it != v.end(); ++it) {
    it->doSomething();
 }

Où T est le type de la classe à l'intérieur du vecteur. Par exemple, si la classe était CActivity, écrivez simplement CActivity au lieu de T.

Ce type de méthode fonctionnera sur chaque STL (pas seulement les vecteurs, ce qui est un peu mieux).

Si vous souhaitez toujours utiliser des index, la méthode est la suivante:

for(std::vector<T>::size_type i = 0; i != v.size(); i++) {
    v[i].doSomething();
}
DiGMi
la source
n'est-ce pas std::vector<T>::size_typetoujours size_t? C'est le type que j'utilise toujours pour ça.
Violet Giraffe
1
@VioletGiraffe Je suis presque sûr que vous avez raison (vous n'avez pas vraiment vérifié), mais c'est une meilleure pratique d'utiliser std :: vector <T> :: size_type.
DiGMi
8

Il y a quelques bonnes raisons d'utiliser des itérateurs, dont certaines sont mentionnées ici:

Changer de conteneur ultérieurement n'invalide pas votre code.

c'est-à-dire que si vous passez d'un std :: vector à un std :: list, ou std :: set, vous ne pouvez pas utiliser d'indices numériques pour obtenir votre valeur contenue. L'utilisation d'un itérateur est toujours valide.

Interception à l'exécution d'une itération non valide

Si vous modifiez votre conteneur au milieu de votre boucle, la prochaine fois que vous utiliserez votre itérateur, il lèvera une exception d'itérateur non valide.

Eddie Parker
la source
1
pourriez-vous nous en référer à certains articles / publications qui expliquent les points ci-dessus avec un exemple de code? ce serait génial! ou si vous pouviez en ajouter un :)
Anu
5

J'ai été surpris que personne n'ait mentionné que l'itération dans un tableau avec un index entier vous permet d'écrire facilement du code défectueux en indiquant un tableau avec le mauvais index. Par exemple, si vous avez des boucles imbriquées utilisant iet jcomme index, vous pouvez indiquer de manière incorrecte un tableau avec jplutôt quei et ainsi introduire une erreur dans le programme.

En revanche, les autres formes répertoriées ici, à savoir la forboucle basée sur la plage et les itérateurs, sont beaucoup moins sujettes aux erreurs. La sémantique du langage et le mécanisme de vérification de type du compilateur vous empêcheront d'accéder accidentellement à un tableau en utilisant le mauvais index.

Diomidis Spinellis
la source
4

Avec STL, les programmeurs utilisent iteratorspour traverser les conteneurs, car l'itérateur est un concept abstrait, implémenté dans tous les conteneurs standard. Par exemple, std::listn'a pas operator []du tout.

Pour toujours
la source
3

L'utilisation de l'opérateur automatique le rend vraiment facile à utiliser car on n'a pas à se soucier du type de données et de la taille du vecteur ou de toute autre structure de données

Vecteur d'itération utilisant la boucle automatique et for

vector<int> vec = {1,2,3,4,5}

for(auto itr : vec)
    cout << itr << " ";

Production:

1 2 3 4 5

Vous pouvez également utiliser cette méthode pour itérer des ensembles et des listes. L'utilisation de auto détecte automatiquement le type de données utilisé dans le modèle et vous permet de l'utiliser. Donc, même si nous avions un vectordes stringou charla même syntaxe fonctionnera bien

Hrishikesh
la source
1

La manière correcte d'itérer la boucle et d'imprimer ses valeurs est la suivante:

#include<vector>

//declare the vector of type int
vector<int> v;

//insert the 5 element in the vector
for ( unsigned int i = 0; i < 5; i++){
    v.push_back(i);
}

//print those element
for (auto it = 0; it < v.end(); i++){
    std::cout << *it << std::endl;
}
Nikhil Rai
la source
1

Voici un moyen plus simple d'itérer et d'imprimer des valeurs en vecteur.

for(int x: A) // for integer x in vector A
    cout<< x <<" "; 
Akram Mohammed
la source
0
 //different declaration type
    vector<int>v;  
    vector<int>v2(5,30); //size is 5 and fill up with 30
    vector<int>v3={10,20,30};
    
    //From C++11 and onwards
    for(auto itr:v2)
        cout<<"\n"<<itr;
     
     //(pre c++11)   
    for(auto itr=v3.begin(); itr !=v3.end(); itr++)
        cout<<"\n"<<*itr;
Bashar
la source