Come navigare in un vettore usando gli iteratori? (C ++)


105

L'obiettivo è accedere all'elemento "nth" di un vettore di stringhe invece dell'operatore [] o del metodo "at". Da quello che ho capito, gli iteratori possono essere usati per navigare attraverso i contenitori, ma non ho mai usato gli iteratori prima e quello che sto leggendo è confuso.

Se qualcuno potesse darmi alcune informazioni su come ottenere questo, lo apprezzerei. Grazie.


I vettori non sono esclusivi dell'STL di C ++? Lo modificherò a prescindere
kevin

kevin: vector è un termine generico che potrebbe essere utilizzato da qualsiasi linguaggio, in particolare quelli legati alla matematica come Mathematica o Matlab.
Gabe

@michael, sì haha ​​l'ho modificato dopo il commento di gabe.
kevin

Risposte:


112

È necessario utilizzare il metodo begine enddella vectorclasse, che restituisce l'iteratore che si riferisce rispettivamente al primo e all'ultimo elemento.

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.

5
Questo non tiene conto del fatto che std::vectorha iteratori ad accesso casuale.
sbi

24
Indipendentemente dal fatto che tu sappia che il tipo di iteratore è ad accesso casuale o meno, il modo "migliore" per spostare un iteratore in avanti di n spazi non è scrivere il tuo ciclo, ma chiamare std::advance(it, n). È definito per fare esattamente ciò che si desidera e verrà utilizzato automaticamente it + nse l'iteratore è contrassegnato come accesso casuale o esegue il ciclo se necessario.
Steve Jessop

61

In genere, gli iteratori vengono utilizzati per accedere agli elementi di un contenitore in modo lineare; tuttavia, con gli "iteratori ad accesso casuale", è possibile accedere a qualsiasi elemento allo stesso modo di operator[].

Per accedere a elementi arbitrari in un vettore vec , puoi utilizzare quanto segue:

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

Di seguito è riportato un esempio di un tipico modello di accesso (versioni precedenti di C ++):

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

Il vantaggio dell'utilizzo dell'iteratore è che puoi applicare lo stesso modello con altri contenitori :

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

Per questo motivo, è davvero facile creare codice modello che funzionerà allo stesso modo indipendentemente dal tipo di contenitore . Un altro vantaggio degli iteratori è che non presume che i dati siano residenti in memoria; per esempio, si potrebbe creare un iteratore in avanti in grado di leggere i dati da un flusso di input o che semplicemente genera dati al volo (ad esempio un intervallo o un generatore di numeri casuali).

Un'altra opzione che usa std::for_eache lambda:

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

Dal momento che C ++ 11 puoi usare autoper evitare di specificare un nome di tipo molto lungo e complicato dell'iteratore come visto prima (o anche più complesso):

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

Inoltre, c'è una variante più semplice per ogni variante:

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

E infine c'è anche std::accumulatedove devi stare attento se stai aggiungendo numeri interi o in virgola mobile.


53

In C ++ - 11 puoi fare:

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';

Vedi qui per le variazioni: https://en.cppreference.com/w/cpp/language/range-for


4
PERCHÉ QUESTO HA ZERO MI PIACE ?! <3
jperl

3
@jperl Inserito con 8 anni di ritardo. Ci vorranno i prossimi 8 anni per ottenere abbastanza voti positivi :)
lashgar

@jperl, beh, la risposta è fuori tema. Sebbene questa funzione di loop sia carina, non ti aiuta a sapere quando sei all'ennesimo elemento, che è la domanda dell'OP. Inoltre, qualsiasi risposta che richieda una complessità temporale O (n), come questa, è pessima. L'accesso all'elemento n-esimo di un vettore dovrebbe sempre essere O (1).
Elliott

@lashgar L'ho provato con array ma non è riuscito. Funziona per array?
era s'q l'

@ eras'q, provato con gcc 7.5.0su Ubuntu 18.04 e funziona per array allo stesso modo.
lashgar

17

Gli iteratori di Vector sono iteratori ad accesso casuale, il che significa che hanno l'aspetto e la sensazione di semplici puntatori.

È possibile accedere all'elemento n-esimo aggiungendo n all'iteratore restituito dal begin()metodo del contenitore oppure è possibile utilizzare 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];

In alternativa puoi usare la funzione Advance che funziona con tutti i tipi di iteratori. (Dovresti considerare se vuoi davvero eseguire "accesso casuale" con iteratori ad accesso non casuale, poiché potrebbe essere una cosa costosa da fare.)

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

std::advance(it, 5);
int sixth = *it;

1
È possibile utilizzarlo anche advanceper iteratori ad accesso casuale o iteratori di categoria sconosciuta, poiché in tal caso è garantito il funzionamento a tempo costante. Questo è il motivo per cui gli iteratori definiti dall'utente dovrebbero essere etichettati correttamente.
Steve Jessop

In effetti, ma advanceè davvero fastidioso da usare (a causa dell'utilizzo del parametro out) se sai di avere a che fare con iteratori ad accesso casuale. Consiglierei solo in codice generico e se non usato molto (se l'algoritmo non supporta bene gli iteratori ad accesso non casuale, così sia - ad esempio, std::sort potrebbe ordinare un std::listma non lo fa perché sarebbe ridicolmente inefficiente ).
UncleBens

Certo, l'esempio classico sarebbe se il tuo algoritmo avesse bisogno solo di un InputIterator, ma per qualsiasi motivo a volte salta avanti, quindi vuoi che sia più efficiente se l'iteratore ha accesso casuale. Non vale la pena limitare il tuo algoritmo all'accesso casuale solo usando operator+. Ma la domanda riguardava esplicitamente il vettore, quindi non c'è niente di sbagliato nella prima parte della tua risposta. Ho solo pensato che la seconda parte potrebbe implicare "non puoi usare anticipo con iteratori ad accesso casuale, anche se lo desideri" a qualcuno che non ha mai visto advanceprima.
Steve Jessop

OK, ho riformulato quel bit e ho fornito l'esempio con un vettore.
UncleBens

seconda riga, Vectordovrebbe essere minuscolo
Lei Yang

0

Ecco un esempio di accesso ithall'indice di un std::vectorutilizzo di un std::iteratorall'interno di un ciclo che non richiede l'incremento di due iteratori.

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());
    }
}

Senza un ciclo for

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

e utilizzando il atmetodo:

printf("Using at position: strs[%d] = %s\n", nth, strs.at(nth).c_str());
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.