Domanda

Qual è il modo corretto di iterare su un vettore in C ++?

Considera questi due frammenti di codice, questo funziona benissimo:

for (unsigned i=0; i < polygon.size(); i++) {
    sum += polygon[i];
}

e questo:

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

che genera warning: comparison between signed and unsigned integer expressions.

Sono nuovo nel mondo del C ++, quindi la unsigned variabile mi sembra un po 'spaventosa e so che <=> le variabili possono essere pericolose se non usate correttamente, quindi - è corretto?

È stato utile?

Soluzione

Iterazione all'indietro

Vedi questa risposta .

Iterating Forward

Questo è quasi identico. Basta cambiare gli iteratori / decrementare lo swap con incrementi. Dovresti preferire gli iteratori. Alcune persone ti dicono di usare std::size_t come tipo di variabile indice. Tuttavia, questo non è portatile. Usa sempre il size_type typedef del contenitore (Mentre potresti cavartela solo con una conversione nel caso di iterazione in avanti, in realtà potrebbe andare storto nel caso di iterazione all'indietro quando usi sizeof, nel caso <=> è più ampio di quello che è il typedef di <=>):

Uso di std :: vector

Uso degli iteratori

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

Importante è, utilizzare sempre il modulo di incremento del prefisso per gli iteratori di cui non si conoscono le definizioni. Ciò assicurerà che il tuo codice sia il più generico possibile.

Uso dell'intervallo C ++ 11

for(auto const& value: a) {
     /* std::cout << value; ... */

Uso degli indici

for(std::vector<int>::size_type i = 0; i != v.size(); i++) {
    /* std::cout << v[i]; ... */
}

Uso di array

Uso degli iteratori

for(element_type* it = a; it != (a + (sizeof a / sizeof *a)); it++) {
    /* std::cout << *it; ... */
}

Uso dell'intervallo C ++ 11

for(std::size_t i = 0; i != (sizeof a / sizeof *a); i++) {
    /* std::cout << a[i]; ... */
}

Uso degli indici

<*>

Leggi nella risposta iterante all'indietro a quale problema l'approccio <=> può comportare, tuttavia.

Altri suggerimenti

Sono passati quattro anni, Google mi ha dato questa risposta. Con lo standard C ++ 11 (aka C ++ 0x ) esiste in realtà un nuovo modo piacevole di farlo (al prezzo di rompere la compatibilità con le versioni precedenti): la nuova auto parola chiave. Ti risparmia il dolore di dover specificare esplicitamente il tipo di iteratore da usare (ripetendo nuovamente il tipo di vettore), quando è ovvio (per il compilatore), quale tipo usare. Con v il tuo vector, puoi fare qualcosa del genere:

for ( auto i = v.begin(); i != v.end(); i++ ) {
    std::cout << *i << std::endl;
}

C ++ 11 va ancora oltre e ti offre una sintassi speciale per iterare raccolte come vettori. Elimina la necessità di scrivere cose sempre uguali:

for ( auto &i : v ) {
    std::cout << i << std::endl;
}

Per vederlo in un programma di lavoro, crea un file auto.cpp:

#include <vector>
#include <iostream>

int main(void) {
    std::vector<int> v = std::vector<int>();
    v.push_back(17);
    v.push_back(12);
    v.push_back(23);
    v.push_back(42);
    for ( auto &i : v ) {
        std::cout << i << std::endl;
    }
    return 0;
}

Al momento della stesura di questo, quando lo compili con g ++ , normalmente devi impostarlo in modo che funzioni con il nuovo standard dando un flag extra:

g++ -std=c++0x -o auto auto.cpp

Ora puoi eseguire l'esempio:

$ ./auto
17
12
23
42

Nota che le istruzioni per la compilazione e l'esecuzione sono specifiche del compilatore gnu c ++ su Linux , il programma dovrebbe essere piattaforma (e compilatore ) indipendente.

Nel caso specifico del tuo esempio, utilizzerei gli algoritmi STL per ottenere questo risultato.

#include <numeric> 

sum = std::accumulate( polygon.begin(), polygon.end(), 0 );

Per un caso più generale, ma comunque abbastanza semplice, sceglierei:

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>

using namespace boost::lambda;
std::for_each( polygon.begin(), polygon.end(), sum += _1 );

Per quanto riguarda la risposta di Johannes Schaub:

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

Potrebbe funzionare con alcuni compilatori ma non con gcc. Il problema qui è la domanda se std :: vector :: iterator è un tipo, una variabile (membro) o una funzione (metodo). Otteniamo il seguente errore con gcc:

In member function ‘void MyClass<T>::myMethod()’:
error: expected `;' before ‘it’
error: ‘it’ was not declared in this scope
In member function ‘void MyClass<T>::sort() [with T = MyClass]’:
instantiated from ‘void MyClass<T>::run() [with T = MyClass]’
instantiated from here
dependent-name ‘std::vector<T*,std::allocator<T*> >::iterator’ is parsed as a non-type, but instantiation yields a type
note: say ‘typename std::vector<T*,std::allocator<T*> >::iterator’ if a type is meant

La soluzione utilizza la parola chiave 'typename' come detto:

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

Una chiamata a vector<T>::size() restituisce un valore di tipo std::vector<T>::size_type, non int, unsigned int o altrimenti.

Anche in genere l'iterazione su un contenitore in C ++ viene eseguita utilizzando iteratori , in questo modo.

std::vector<T>::iterator i = polygon.begin();
std::vector<T>::iterator end = polygon.end();

for(; i != end; i++){
    sum += *i;
}

Dove T è il tipo di dati archiviati nel vettore.

O usando i diversi algoritmi di iterazione (std::transform, std::copy, std::fill, std::for_each eccetera).

Usa size_t:

for (size_t i=0; i < polygon.size(); i++)

Citando Wikipedia :

  

I file di intestazione stdlib.h e stddef.h definiscono un tipo di dati chiamato <=> che viene utilizzato per rappresentare la dimensione di un oggetto. Le funzioni di libreria che assumono dimensioni si aspettano che siano di tipo <=> e l'operatore sizeof restituisce <=>.

     

Il tipo effettivo di <=> dipende dalla piattaforma; un errore comune è supporre che <=> sia uguale a unsigned int, il che può portare a errori di programmazione, in particolare quando le architetture a 64 bit diventano più diffuse.

Di solito uso BOOST_FOREACH:

#include <boost/foreach.hpp>

BOOST_FOREACH( vector_type::value_type& value, v ) {
    // do something with 'value'
}

Funziona su contenitori STL, array, stringhe in stile C, ecc.

Un po 'di storia:

Per indicare se un numero è negativo o no, utilizzare un bit 'segno'. int è un tipo di dati con segno che significa che può contenere valori positivi e negativi (da circa -2 miliardi a 2 miliardi). Unsigned può solo memorizzare numeri positivi (e poiché non spreca un po 'sui metadati, può archiviare più: da 0 a circa 4 miliardi).

std::vector::size() restituisce un unsigned, come può un vettore avere una lunghezza negativa?

L'avvertimento ti dice che l'operando di destra della tua dichiarazione di disuguaglianza può contenere più dati di quello di sinistra.

Fondamentalmente se hai un vettore con più di 2 miliardi di voci e usi un numero intero per indicizzare, colpirai i problemi di overflow (l'int tornerà indietro a 2 miliardi negativi).

Per essere completo, la sintassi C ++ 11 abilita solo un'altra versione per gli iteratori ( ref ):

for(auto it=std::begin(polygon); it!=std::end(polygon); ++it) {
  // do something with *it
}

Che è anche comodo per l'iterazione inversa

for(auto it=std::end(polygon)-1; it!=std::begin(polygon)-1; --it) {
  // do something with *it
}

In C ++ 11

Vorrei utilizzare algoritmi generali come for_each per evitare la ricerca del giusto tipo di iteratore ed espressione lambda per evitare funzioni / oggetti extra denominati.

Il breve " pretty " esempio per il tuo caso particolare (supponendo che il poligono sia un vettore di numeri interi):

for_each(polygon.begin(), polygon.end(), [&sum](int i){ sum += i; });

testato su: http://ideone.com/i6Ethd

Non dimenticare di includere: algoritmo e, naturalmente, vettore :)

Microsoft ha in realtà anche un bell'esempio su questo:
fonte: http://msdn.microsoft.com/en -us / library / dd293608.aspx

#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;

int main() 
{
   // Create a vector object that contains 10 elements.
   vector<int> v;
   for (int i = 1; i < 10; ++i) {
      v.push_back(i);
   }

   // Count the number of even numbers in the vector by 
   // using the for_each function and a lambda.
   int evenCount = 0;
   for_each(v.begin(), v.end(), [&evenCount] (int n) {
      cout << n;
      if (n % 2 == 0) {
         cout << " is even " << endl;
         ++evenCount;
      } else {
         cout << " is odd " << endl;
      }
   });

   // Print the count of even numbers to the console.
   cout << "There are " << evenCount 
        << " even numbers in the vector." << endl;
}
for (vector<int>::iterator it = polygon.begin(); it != polygon.end(); it++)
    sum += *it; 

Il primo è di tipo corretto e corretto in un certo senso. (Se ci pensi, la dimensione non può mai essere inferiore a zero.) Questo avvertimento mi sembra uno dei buoni candidati per essere ignorato, però.

Valuta se devi iterare affatto

L'intestazione standard <algorithm> ci fornisce le funzionalità per questo:

using std::begin;  // allows argument-dependent lookup even
using std::end;    // if the container type is unknown here
auto sum = std::accumulate(begin(polygon), end(polygon), 0);

Altre funzioni nella libreria degli algoritmi svolgono attività comuni: assicurati di sapere cosa è disponibile se vuoi risparmiare.

Dettaglio oscuro ma importante: se dici " per (auto it) " come segue, ottieni una copia dell'oggetto, non l'elemento reale:

struct Xs{int i} x;
x.i = 0;
vector <Xs> v;
v.push_back(x);
for(auto it : v)
    it.i = 1;         // doesn't change the element v[0]

Per modificare gli elementi del vettore, è necessario definire l'iteratore come riferimento:

for(auto &it : v)

Se il compilatore lo supporta, è possibile utilizzare un intervallo basato su per accedere agli elementi vettoriali:

vector<float> vertices{ 1.0, 2.0, 3.0 };

for(float vertex: vertices){
    std::cout << vertex << " ";
}

Stampe: 1 2 3. Nota, non puoi usare questa tecnica per cambiare gli elementi del vettore.

I due segmenti di codice funzionano allo stesso modo. Tuttavia, unsigned int & Quot; il percorso è corretto. L'uso di tipi int senza segno funzionerà meglio con il vettore nell'istanza in cui lo hai utilizzato. Chiamare la funzione membro size () su un vettore restituisce un valore intero senza segno, quindi si desidera confrontare la variabile & Quot; i & Quot; a un valore del proprio tipo.

Inoltre, se sei ancora un po 'a disagio su come " unsigned int " cerca nel tuo codice, prova " uint " ;. Questa è sostanzialmente una versione abbreviata di & Quot; unsigned int & Quot; e funziona esattamente lo stesso. Inoltre non è necessario includere altre intestazioni per usarlo.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top