Scorrere un vettore C ++ usando un ciclo 'for'


140

Sono nuovo nel linguaggio C ++. Ho iniziato a utilizzare i vettori e ho notato che in tutto il codice vedo iterare attraverso un vettore tramite indici, il primo parametro del forciclo è sempre qualcosa basato sul vettore. In Java potrei fare qualcosa del genere con una ArrayList:

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

C'è un motivo per cui non lo vedo in C ++? È una cattiva pratica?


1
Il ciclo for non è una funzione, quindi non ha parametri (o argomenti, che è ciò che si passa). Intendi qualcosa del genere std::vector<int>::size_type i = 0;, comunque o forse std::vector<int>::iterator it = vector.begin();?
chris,

Esatto, tutti gli esempi che vedo sono scritti in questo modo.
Flynn,

4
In Java, preferirei un ciclo per ogni o utilizzare iteratori. Praticamente uguale a C ++ anche se sintassi leggermente diversa.
Jesse Good,


10
La maggior parte delle risposte qui erroneamente presuppone che la Q sia: Qual è il modo migliore / più breve per scorrere std::vector? , l'attuale Q che viene chiesto qui è: c'è qualche motivo per cui non vedo questo in C ++? È una cattiva pratica? aka Perché vedo sempre il codice in C ++ che utilizza iteratori durante l'iterazione std::vector?
Risparmia il

Risposte:


93

C'è qualche motivo per cui non lo vedo in C ++? È una cattiva pratica?

No. Non è una cattiva pratica, ma il seguente approccio rende al tuo codice una certa flessibilità .

Di solito, pre-C ++ 11 il codice per iterare su elementi contenitore utilizza iteratori, qualcosa del tipo:

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

Questo perché rende il codice più flessibile.

Tutti i contenitori di librerie standard supportano e forniscono iteratori. Se in un momento successivo dello sviluppo è necessario passare a un altro contenitore, non è necessario modificare questo codice.

Nota: la scrittura di codice che funziona con ogni possibile contenitore di libreria standard non è così semplice come potrebbe sembrare.


25
Qualcuno potrebbe spiegarmi perché in questo particolare frammento di codice / caso consigli gli iteratori sull'indicizzazione? Di quale "flessibilità" stai parlando? Personalmente, non mi piacciono gli iteratori, gonfiano il codice - semplicemente più caratteri da digitare per lo stesso effetto. Soprattutto se non puoi usarlo auto.
Violet Giraffe,

8
@VioletGiraffe: Durante l'utilizzo degli iteratori è difficile sbagliare in alcuni casi come intervalli vuoti e il codice è più dettagliato. Naturalmente è una questione o percezione e scelta, quindi può essere dibattuto all'infinito.
Risparmia il

9
Perché mostri solo come dichiarare l'iteratore ma non come usarlo per fare il ciclo ...?
underscore_d

116

Il motivo per cui non vedi tale pratica è abbastanza soggettivo e non può avere una risposta definitiva, perché ho visto molti dei codici che usano il tuo modo menzionato piuttosto che il iteratorcodice di stile.

In seguito può essere motivi di persone non considerando vector.size()modo di looping:

  1. Essere paranoici nel chiamare size()ogni volta nella condizione di loop. Tuttavia o non è un problema o può essere banalmente risolto
  2. Preferendo std::for_each()oltre il forciclo stesso
  3. In seguito, la modifica del contenitore da std::vectorun altro (ad es map. list) Richiederà anche la modifica del meccanismo di loop, poiché non tutti i container supportano lo size()stile di loop

C ++ 11 offre una buona funzione per spostarsi attraverso i contenitori. Questo si chiama "range based for loop" (o "migliorato per loop" in Java).

Con un piccolo codice puoi attraversare l'intero (obbligatorio!) std::vector:

vector<int> vi;
...
for(int i : vi) 
  cout << "i = " << i << endl;

12
Solo per notare un piccolo svantaggio del range basato su loop : non puoi usarlo con #pragma omp parallel for.
liborm

2
Mi piace la versione compatta perché c'è meno codice da leggere. Una volta effettuata la regolazione mentale, è molto più facile da capire e gli errori si distinguono di più. Lo rende anche molto più ovvio quando si verifica un'iterazione non standard perché c'è un pezzo di codice molto più grande.
Abominatore del codice

87

Il modo più pulito di iterare attraverso un vettore è tramite iteratori:

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

o (equivalente a quanto sopra)

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

Prima di C ++ 0x, è necessario sostituire auto con il tipo di iteratore e utilizzare le funzioni membro anziché l'inizio e la fine delle funzioni globali.

Questo probabilmente è quello che hai visto. Rispetto all'approccio che menzioni, il vantaggio è che non dipendi molto dal tipo di vector. Se si vectorpassa a una diversa classe "tipo di raccolta", il codice probabilmente funzionerà comunque. Tuttavia, puoi fare qualcosa di simile anche in Java. Non c'è molta differenza concettualmente; C ++, tuttavia, utilizza i modelli per implementarlo (rispetto ai generici in Java); quindi l'approccio funzionerà per tutti i tipi per i quali sono definite begine le endfunzioni, anche per tipi non di classe come gli array statici. Vedi qui: In che modo funziona il range-based per array semplici?


5
auto, inizio / fine liberi sono anche C ++ 11. E anche, dovresti usarlo ++, anziché it ++ in molti casi.
vigilia il

Sì hai ragione. L'implementazione begine end, tuttavia, è un one-liner.
John B

@JohnB è più di una riga, perché funziona anche per array di dimensioni fisse. autod'altra parte sarebbe piuttosto complicato.
juanchopanza,

Se ne hai bisogno solo per il vettore è una linea.
Giovanni B

Tuttavia, il primo esempio è fuorviante, dal momento che non può funzionare in C ++ 03, mentre il tuo fraseggio suggerisce che funziona.
juanchopanza,

35

Il modo giusto per farlo è:

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

Dove T è il tipo di classe all'interno del vettore. Ad esempio, se la classe era CActivity, basta scrivere CActivity invece di T.

Questo tipo di metodo funzionerà su ogni STL (non solo vettori, che è un po 'meglio).

Se vuoi ancora usare gli indici, il modo è:

for(std::vector<T>::size_type i = 0; i != v.size(); i++) {
    v[i].doSomething();
}

non è std::vector<T>::size_typesempre size_t? Questo è il tipo che uso sempre per questo.
Violet Giraffe,

1
@VioletGiraffe Sono abbastanza sicuro che tu abbia ragione (non ho davvero controllato), ma è una buona pratica usare std :: vector <T> :: size_type.
DiGMi,

8

Esistono un paio di validi motivi per utilizzare gli iteratori, alcuni dei quali sono menzionati qui:

Il cambio successivo dei contenitori non invalida il codice.

cioè, se passi da uno std :: vector a uno std :: list, o std :: set, non puoi usare gli indici numerici per ottenere il tuo valore contenuto. L'uso di un iteratore è ancora valido.

Runtime catching di iterazioni non valide

Se modifichi il tuo contenitore nel mezzo del tuo ciclo, la prossima volta che usi l'iteratore genererà un'eccezione di iteratore non valida.


1
potresti indicare qualche articolo / post che spieghiamo i punti sopra con un codice di esempio? sarebbe bello! o se potessi aggiungerne uno :)
Anu,

5

Sono rimasto sorpreso dal fatto che nessuno abbia mai detto che iterando attraverso un array con un indice intero è facile scrivere codice difettoso iscrivendo un array con un indice sbagliato. Ad esempio, se hai cicli nidificati che utilizzano ie jcome indici, potresti abbonare erroneamente un array con jpiuttosto che ie quindi introdurre un errore nel programma.

Al contrario, le altre forme elencate qui, ovvero il forloop basato su range e gli iteratori, sono molto meno soggette a errori. La semantica della lingua e il meccanismo di controllo del tipo del compilatore ti impediranno di accedere accidentalmente a un array usando l'indice errato.


4

Con STL, i programmatori usano iteratorsper attraversare i contenitori, poiché iteratore è un concetto astratto, implementato in tutti i contenitori standard. Ad esempio, std::listnon ha operator []affatto.


3

L'uso dell'operatore automatico lo rende davvero facile da usare in quanto non è necessario preoccuparsi del tipo di dati e delle dimensioni del vettore o di qualsiasi altra struttura di dati

Iterazione del vettore mediante auto e per loop

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

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

Produzione:

1 2 3 4 5

È inoltre possibile utilizzare questo metodo per iterare set ed elenchi. L'uso di auto rileva automaticamente il tipo di dati utilizzato nel modello e consente di utilizzarlo. Quindi, anche se abbiamo avuto un vectordi stringo charla stessa sintassi funzionano bene


1

Il modo corretto di iterare il ciclo e stamparne i valori è il seguente:

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

1

Ecco un modo più semplice per iterare e stampare valori in formato vettoriale.

for(int x: A) // for integer x in vector A
    cout<< x <<" "; 

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;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.