Anche se @JamesMcNellis risposta è uno valido vorrei spiegare qualcosa sulla gestione degli errori e anche il fatto che c'è un altro modo di fare ciò che si vuole.
Ci sono tre modi di accesso a un elemento specifico in un vettore:
- Utilizzando la
[]
operatore
- Utilizzando la funzione di membro
at(...)
- Utilizzando un iteratore in combinazione con un dato di offset
Negli esempi seguenti utilizzerò il seguente vettore come topo da laboratorio:
static const int arr[] = {1, 2, 3, 4};
std::vector<int> v(arr, arr+sizeof(arr)/sizeof(arr[0]));
Questo crea un vettore come si vede qui sotto:
1 2 3 4
prima cosa diamo un'occhiata al []
modo di fare le cose. Funziona più o meno nello stesso modo in cui ti aspetti lavorando con un array normale. Si fornisce un indice e possibilmente si accede all'elemento desiderato. Dico possibilmente perché l'operatore []
non controlla se il vettore contiene effettivamente molti elementi. Ciò porta a un accesso non valido alla memoria non valida. Esempio:
v[10] = 9;
Ciò può causare o non causare un arresto anomalo istantaneo. Il caso peggiore è ovviamente se non lo fa e in effetti ottieni quello che sembra essere un valore valido. Analogamente agli array, questo potrebbe portare a perdite di tempo nel tentativo di trovare il motivo per cui 1000 righe di codice in un secondo momento otterrete il valore di 100
anziché 234
, che è in qualche modo collegato a quella stessa posizione in cui si recupera un elemento dal proprio vettore.
Un modo molto migliore è utilizzare at(...)
. Verificherà automaticamente il comportamento out of bounds
e interromperà il lancio di un numero std::out_of_range
. Quindi, nel caso in cui abbiamo
v.at(10) = 9;
Avremo:
Termina chiamata dopo aver lanciato un caso di 'std :: out_of_range'
cosa(): vector :: _ M_range_check: __n (che è 10)> = this-> dimensioni() (che è 4)
il terzo modo è simile al []
operatore nel senso che è possibile avvitare le cose. Un vettore come un array è una sequenza di blocchi di memoria continua contenenti dati dello stesso tipo. Ciò significa che è possibile utilizzare l'indirizzo di partenza assegnandolo a un iteratore e quindi aggiungere un offset a questo iteratore. L'offset semplicemente l'acronimo di quanti elementi dopo il primo elemento da attraversare:
std::vector<int>::iterator it = v.begin(); // First element of your vector
*(it+0) = 9; // offest = 0 basically means accessing v.begin()
// Now we have 9 2 3 4 instead of 1 2 3 4
*(it+1) = -1; // offset = 1 means first item of v plus an additional one
// Now we have 9 -1 3 4 instead of 9 2 3 4
// ...
Come potete vedere possiamo anche fare
*(it+10) = 9;
che è ancora un accesso alla memoria non valida. Questo è fondamentalmente lo stesso di usare at(0 + offset)
ma senza il controllo degli errori fuori dai limiti.
Suggerirei di utilizzare at(...)
quando possibile non solo perché è più leggibile rispetto all'accesso iteratore ma a causa del controllo degli errori per l'indice non valido che ho menzionato sopra per l'iteratore con la combinazione di offset e l'operatore []
.
EDIT: ho dimenticato il std::for_each
dal algorithm
intestazione della libreria standard C++. Questo è un altro modo che posso raccomandare (usa internamente un iteratore). Puoi leggere ulteriori informazioni al riguardo, ad esempio here.
Qualsiasi problema con l [4] = -1; Oppure l.at (4) = -1, se preferisci. –