Comment naviguer dans un vecteur à l'aide d'itérateurs? (C ++)

105

Le but est d'accéder au "nième" élément d'un vecteur de chaînes au lieu de l'opérateur [] ou de la méthode "at". D'après ce que je comprends, les itérateurs peuvent être utilisés pour naviguer dans les conteneurs, mais je n'ai jamais utilisé d'itérateurs auparavant, et ce que je lis est déroutant.

Si quelqu'un pouvait me donner des informations sur la façon d'y parvenir, je l'apprécierais. Je vous remercie.

Kevin
la source
Les vecteurs ne sont-ils pas exclusifs à la STL de C ++? Je l'éditerai quand même
Kevin
kevin: vector est un terme générique qui pourrait être utilisé par n'importe quel langage, en particulier ceux liés aux mathématiques comme Mathematica ou Matlab.
Gabe
@michael, ouais haha ​​je l'ai édité après le commentaire de gabe.
kevin

Réponses:

112

Vous devez utiliser la méthode beginand endde la vectorclasse, qui renvoie l'itérateur faisant référence respectivement au premier et au dernier élément.

using namespace std;  

vector<string> myvector;  // a vector of stings.


// push some strings in the vector.
myvector.push_back("a");
myvector.push_back("b");
myvector.push_back("c");
myvector.push_back("d");


vector<string>::iterator it;  // declare an iterator to a vector of strings
int n = 3;  // nth element to be found.
int i = 0;  // counter.

// now start at from the beginning
// and keep iterating over the element till you find
// nth element...or reach the end of vector.
for(it = myvector.begin(); it != myvector.end(); it++,i++ )    {
    // found nth element..print and break.
    if(i == n) {
        cout<< *it << endl;  // prints d.
        break;
    }
}

// other easier ways of doing the same.
// using operator[]
cout<<myvector[n]<<endl;  // prints d.

// using the at method
cout << myvector.at(n) << endl;  // prints d.
codaddict
la source
5
Cela manque le fait qui std::vectora des itérateurs à accès aléatoire.
sbi
24
Que vous sachiez que le type d'itérateur est à accès aléatoire ou non, la "meilleure" façon de faire avancer un itérateur de n espaces n'est pas d'écrire votre propre boucle, mais d'appeler std::advance(it, n). Il est défini pour faire exactement ce que vous voulez, et il l'utilisera automatiquement it + nsi l'itérateur est étiqueté comme à accès aléatoire, ou fera la boucle s'il le faut.
Steve Jessop
61

En règle générale, les itérateurs sont utilisés pour accéder aux éléments d'un conteneur de manière linéaire; cependant, avec des "itérateurs à accès aléatoire", il est possible d'accéder à n'importe quel élément de la même manière que operator[].

Pour accéder à des éléments arbitraires dans un vecteur vec , vous pouvez utiliser ce qui suit:

vec.begin()                  // 1st
vec.begin()+1                // 2nd
// ...
vec.begin()+(i-1)            // ith
// ...
vec.begin()+(vec.size()-1)   // last

Voici un exemple de modèle d'accès typique (versions antérieures de C ++):

int sum = 0;
using Iter = std::vector<int>::const_iterator;
for (Iter it = vec.begin(); it!=vec.end(); ++it) {
    sum += *it;
}

L'avantage d'utiliser iterator est que vous pouvez appliquer le même modèle avec d'autres conteneurs :

sum = 0;
for (Iter it = lst.begin(); it!=lst.end(); ++it) {
    sum += *it;
}

Pour cette raison, il est vraiment facile de créer un code de modèle qui fonctionnera de la même manière quel que soit le type de conteneur . Un autre avantage des itérateurs est qu'ils ne supposent pas que les données résident en mémoire; par exemple, on pourrait créer un itérateur avant qui peut lire des données à partir d'un flux d'entrée, ou qui génère simplement des données à la volée (par exemple un générateur de plage ou de nombres aléatoires).

Une autre option utilisant std::for_eachet lambdas:

sum = 0;
std::for_each(vec.begin(), vec.end(), [&sum](int i) { sum += i; });

Depuis C ++ 11, vous pouvez utiliser autopour éviter de spécifier un nom de type très long et compliqué de l'itérateur comme vu précédemment (ou même plus complexe):

sum = 0;
for (auto it = vec.begin(); it!=vec.end(); ++it) {
    sum += *it;
}

Et, en plus, il existe une variante plus simple pour chaque variante:

sum = 0;
for (auto value : vec) {
    sum += value;
}

Et enfin, il y a aussi std::accumulatelà où vous devez faire attention si vous ajoutez des nombres entiers ou flottants.

Michael Aaron Safyan
la source
53

En C ++ - 11, vous pouvez faire:

std::vector<int> v = {0, 1, 2, 3, 4, 5};
for (auto i : v)
{
   // access by value, the type of i is int
   std::cout << i << ' ';
}
std::cout << '\n';

Voir ici pour les variations: https://en.cppreference.com/w/cpp/language/range-for

fouet
la source
4
POURQUOI CELA N'A-T-IL AUCUN AVIS?! <3
jperl
3
@jperl Publié avec 8 ans de retard. Il faudra les 8 prochaines années pour obtenir suffisamment de votes positifs :)
lashgar
@jperl, eh bien la réponse est hors sujet. Bien que cette fonction de boucle soit agréable, elle ne vous aide pas à savoir quand vous êtes au nième élément, qui est la question du PO. De plus, toute réponse qui nécessite une complexité en temps O (n), comme celle-ci, est très mauvaise. L'accès au nième élément d'un vecteur doit toujours être O (1).
Elliott
@lashgar J'ai essayé cela avec un tableau mais j'ai échoué. Cela fonctionne-t-il pour le tableau?
era s'q
@ eras'q, essayé avec gcc 7.5.0sur Ubuntu 18.04 et fonctionne de la même manière pour les tableaux.
lashgar
17

Les itérateurs de Vector sont des itérateurs à accès aléatoire, ce qui signifie qu'ils ressemblent à des pointeurs simples.

Vous pouvez accéder au nième élément en ajoutant n à l'itérateur renvoyé par la begin()méthode du conteneur , ou vous pouvez utiliser operator [].

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

int sixth = *(it + 5);
int third = *(2 + it);
int second = it[1];

Vous pouvez également utiliser la fonction avancée qui fonctionne avec toutes sortes d'itérateurs. (Vous devez vous demander si vous voulez vraiment effectuer un "accès aléatoire" avec des itérateurs à accès non aléatoire, car cela pourrait être une chose coûteuse à faire.)

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

std::advance(it, 5);
int sixth = *it;
OncleBens
la source
1
Vous pouvez également l'utiliser advancepour des itérateurs à accès aléatoire, ou des itérateurs de catégorie inconnue, car il est garanti de fonctionner en temps constant dans ce cas. C'est pourquoi les itérateurs définis par l'utilisateur doivent être correctement balisés.
Steve Jessop
En effet, mais advancec'est vraiment ennuyeux à utiliser (à cause de l'utilisation du paramètre out) si vous savez que vous avez affaire à des itérateurs à accès aléatoire. Je ne le recommanderais que dans du code générique, et s'il n'est pas beaucoup utilisé (si l'algorithme ne prend pas bien en charge les itérateurs à accès non aléatoire, qu'il en soit ainsi - par exemple, std::sort pourrait trier un std::listmais ce n'est pas le cas car ce serait ridiculement inefficace ).
UncleBens
Bien sûr, l'exemple classique serait si votre algorithme n'a besoin que d' un InputIterator, mais pour une raison quelconque, il saute parfois en avant, vous voulez donc qu'il soit plus efficace si l'itérateur a un accès aléatoire. Cela ne vaut pas la peine de restreindre votre algorithme à un accès aléatoire uniquement en utilisant operator+. Mais la question concernait explicitement le vecteur, il n'y a donc rien de mal avec la première partie de votre réponse. J'ai juste pensé que la deuxième partie pourrait impliquer "vous ne pouvez pas utiliser l'avance avec des itérateurs à accès aléatoire, même si vous le souhaitez" à quelqu'un qui n'a jamais vu advanceauparavant.
Steve Jessop
OK, reformulé ce bit et donné l'exemple avec un vecteur.
UncleBens
deuxième ligne, Vectordevrait être en minuscules
Lei Yang
0

Voici un exemple d'accès à l' ithindex d'un en std::vectorutilisant un std::iteratordans une boucle qui ne nécessite pas d'incrémenter deux itérateurs.

std::vector<std::string> strs = {"sigma" "alpha", "beta", "rho", "nova"};
int nth = 2;
std::vector<std::string>::iterator it;
for(it = strs.begin(); it != strs.end(); it++) {
    int ith = it - strs.begin();
    if(ith == nth) {
        printf("Iterator within  a for-loop: strs[%d] = %s\n", ith, (*it).c_str());
    }
}

Sans boucle for

it = strs.begin() + nth;
printf("Iterator without a for-loop: strs[%d] = %s\n", nth, (*it).c_str());

et en utilisant la atméthode:

printf("Using at position: strs[%d] = %s\n", nth, strs.at(nth).c_str());
hmofrad
la source