2012-10-03 14 views
83

Sono nuovo nel linguaggio C++. Ho iniziato a utilizzare i vettori e ho notato che in tutto il codice che vedo per scorrere un vettore tramite indici, il primo parametro del ciclo for è sempre qualcosa basato sul vettore. In Java Potrei fare qualcosa di simile con un ArrayList:Iterate attraverso un vettore C++ usando un ciclo 'for'

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

C'è un motivo non vedo questo in C++? È una cattiva pratica?

+0

Il ciclo for non è una funzione, quindi non ha parametri (o argomenti, che è ciò che si inoltra). Intendi qualcosa come 'std :: vector :: size_type i = 0;', però, o forse 'std :: vector :: iterator it = vector.begin();'? – chris

+0

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

+4

In Java, preferirei un ciclo for-each o utilizzare iteratori. Praticamente identico al C++, sebbene una sintassi leggermente diversa. –

risposta

59

è un qualsiasi motivo non vedo questo in C++:

Se si desidera continuare a utilizzare gli indici, il modo in cui è? È una cattiva pratica?

No. Non è una cattiva pratica, ma rende il codice certa flessibilità .

Di solito, pre-C++ 11 il codice per iterare su elementi contenitori usa iteratori, qualcosa di simile:

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

Questo è perché rende il codice più flessibile.

Tutti i contenitori di libreria standard supportano e forniscono iteratori e dato che se in un momento successivo di sviluppo è necessario passare a un altro contenitore, questo codice non deve essere modificato.

Nota: Il codice di scrittura che funziona con tutti i contenitori di libreria standard possibili non è così facilmente possibile come potrebbe sembrare apparentemente.

+16

Qualcuno potrebbe spiegarmi perché in questo caso particolare/snippet di codice si consiglia agli iteratori di indicizzare? Di che cosa parla questa "flessibilità"? Personalmente, non mi piacciono gli iteratori, gonfiano il codice - semplicemente più caratteri da digitare per lo stesso effetto. Soprattutto se non puoi usare 'auto'. –

+5

@VioletGiraffe: Durante l'utilizzo di iteratori è difficile sbagliare in determinati casi come intervalli vuoti e il codice è più dettagliato. Il suo è una questione, una percezione e una scelta, quindi può essere discusso all'infinito. –

50

Il modo più pulito di iterazione attraverso un vettore è via 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, si deve sostituire auto da il tipo iteratore e utilizza le funzioni membro invece delle funzioni globali iniziano e terminano.

Questo probabilmente è quello che hai visto. Rispetto all'approccio che citi, il vantaggio è che non si dipende molto dal tipo di vector. Se si modifica vector in una diversa classe "tipo-raccolta", il codice probabilmente funzionerà ancora. Puoi, tuttavia, 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 le funzioni begin e end, anche per i tipi non di classe come gli array statici. Vedi qui: How does the range-based for work for plain arrays?

+4

auto, inizio/fine gratuiti sono anche C++ 11. Inoltre, dovresti usare ++ it, invece di ++ in molti casi. – ForEveR

+0

Sì, hai ragione. L'implementazione di 'begin' e' end', tuttavia, è un one-liner. – JohnB

+0

@JohnB è una fodera più di una, perché funziona anche con matrici di dimensioni fisse. "auto" d'altra parte sarebbe abbastanza difficile. – juanchopanza

27

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, scrivi CActivity anziché T.

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

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

non è 'std :: vector :: size_type' sempre' size_t'? Questo è il tipo che uso sempre per questo. –

+1

@VioletGiraffe Sono abbastanza sicuro che tu abbia ragione (non ho ancora controllato), ma è una pratica migliore usare std :: vector :: size_type. – DiGMi

+0

grazie per aver mostrato il modo indice "vietato". – Rosenthal

4

Con STL, i programmatori utilizzano iterators per attraversare contenitori, poiché iteratore è un concetto astratto, implementato in tutti i contenitori standard. Ad esempio, std::list non ha alcun operator [].

67

Il motivo per cui non si vede tale pratica è molto soggettivo e non può avere una risposta definita, perché ho visto molti dei codici che usano il modo menzionato piuttosto che il codice di stile iterator.

seguito possono essere motivi di persone non considerando vector.size() modo di looping:

  1. Essere paranoici di chiamare size() ogni volta nel ciclo condizione. Tuttavia o si tratta di un non-problema oppure può essere banalmente fisso
  2. Preferendo std::for_each() sul for loop stesso
  3. Successivamente cambiare il contenitore dal std::vector a altra (es map, list) sarà anche richiedere la modifica della annodare meccanismo, perché non ogni contenitore sostegno size() stile di loop

C++ 11 fornisce una buona possibilità di muoversi attraverso i contenitori. Questo è chiamato "range based for loop" (o "enhanced for loop" in Java).

Con po 'di codice è possibile attraversare attraverso la piena std::vector (obbligatorio!):

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

Solo per notare un piccolo svantaggio di _range based for loop_: non puoi usarlo con '#pragma omp parallel for'. – liborm

+1

Mi piace la versione compatta perché c'è meno codice da leggere. Una volta apportate le regolazioni mentali è molto più facile da capire e gli insetti si distinguono di più. Rende anche più ovvio quando si verifica un'iterazione non standard perché c'è una porzione molto più grande di codice. –

5

Ci sono un paio di forti ragioni per usare iteratori, alcuni dei quali sono menzionati qui:

contenitori di commutazione in seguito non invalida il tuo codice.

Ad esempio, se si passa da std :: vector a std :: list o std :: set, non è possibile utilizzare indici numerici per ottenere il valore contenuto. L'utilizzo di un iteratore è ancora valido.

Runtime cattura di iterazione non valida

Se si modifica il contenitore nel mezzo del vostro ciclo, la prossima volta che si utilizza iteratore sarà un'eccezione iteratore non valido.

Problemi correlati