Esiste un contenitore limitato nella libreria C ++ che deve essere implementato con memoria contigua. vector
è uno di questi, quindi, dato un riferimento al primo elemento, è ben noto dove il prossimo elemento è in memoria e anche il modo per arrivarci è ben noto.
Per gli altri contenitori, non si sa come viene depositato in memoria, dove sono tutti gli elementi e come passare da un elemento all'altro. Gli iteratori forniscono quel meccanismo;
- Dov'è il prossimo elemento (
++
), probabilmente dov'è l'elemento precedente ( --
) e anche, ottieni l'elemento 3 posti lontano da quello corrente ( += 3
)
- Sa come arrivare al valore dell'elemento a cui si fa riferimento (dereferenziare l'iteratore)
- Sa anche quando arriva alla fine del contenitore (se confrontato con
.end()
)
Una volta stabilita la semantica precedente, diventa molto facile scrivere algoritmi generali che sono agnostici del contenitore utilizzato e del layout della memoria.
Gli algoritmi generali possono anche applicare alcune ottimizzazioni conoscendo la natura degli iteratori (iteratori casuali e iteratori diretti) e il tipo iteratore è incorporato nell'iteratore stesso. Certo, sono presenti algoritmi più specializzati che conoscono il layout della memoria e che possono essere ulteriormente ottimizzati, ma in genere finiscono per essere membri dei container.
Nota: è possibile accedere agli elementi facendo riferimento a membri come .front()
, .back()
e .at()
ecc. supportati dal contenitore. Il supporto per questi è definito per contenitore, e il fronte e il retro arrivano solo al primo e all'ultimo elemento, il supporto generale dell'algoritmo e l'utilizzo di questi membri è limitato.
Nota sulla resize
: la chiamata per ridimensionare il vettore potrebbe invalidare sia gli iteratori che i riferimenti agli elementi (a seconda che si verifichi una riallocazione) e in genere si dovrebbe presumere che il ridimensionamento faccia li invalida.
Una tecnica comune quando si utilizzano gli iteratori consiste nel loop dall'inizio alla fine, dato il ciclo for
nel campione, come segue:
// auto used for the sample
// if the container is non-const, then "it" is of type
// std::vector<int>::iterator
for (auto it = market.vec.begin(); it != market.vec.end(); ++it)
// code
Inoltre, gli indici, ecc. potrebbero essere aggiunti come richiesto. Dato il codice di esempio, std::iota
potrebbe essere un buon sostituto anche per questo.
std::iota(market.vec.begin(), market.vec.end(), 0);
Se si prende in considerazione l'esatto codice di esempio fornito, vi sono pochi motivi per usare l'iteratore o il riferimento, basta accedere all'elemento nel vettore usando l'operatore indice []
. Non vedo alcun codice di sincronizzazione in modo che il vettore non venga ridimensionato mentre viene eseguito il ciclo for
.
Detto questo, è preferibile lavorare con gli iteratori e dereferenziarli per ottenere il valore dell'elemento.