ProgrammazioneSviluppatore C++

Che cosa sono gli iteratori in C++ STL, quali tipi esistono e quali sfumature dell'uso devono essere conosciute durante la programmazione?

Supera i colloqui con l'assistente IA Hintsage

Risposta

Gli iteratori sono oggetti che consentono di riferirsi agli elementi dei contenitori STL in modo simile ai puntatori. Forniscono un modo unificato di accesso agli elementi di qualsiasi contenitore (vector, list, map, ecc.). Gli iteratori sono di vari tipi:

  • InputIterator — per la lettura della sequenza in avanti.
  • OutputIterator — per la scrittura dei valori della sequenza.
  • ForwardIterator — per la lettura e la scrittura in avanti (vive più a lungo di un OutputIterator).
  • BidirectionalIterator — consente di muoversi sia in avanti che all'indietro.
  • RandomAccessIterator — supporta accesso casuale (ad esempio, std::vector::iterator).

È fondamentale prestare attenzione alla vita degli iteratori:

  • Gli iteratori dei contenitori possono essere invalidati (diventare non validi) quando il contenitore viene modificato. Ad esempio, l'inserimento di elementi in un vettore può rendere gli iteratori precedenti non validi.
  • Diversi contenitori gestiscono diversamente il ciclo di vita degli iteratori. In std::list, l'inserimento o la rimozione non invalidano altri iteratori, mentre in std::vector — quasi sempre invalidano.

Esempio:

std::vector<int> v = {1,2,3,4,5}; for (auto it = v.begin(); it != v.end(); ++it) { if (*it == 3) { // Rimuoviamo l'elemento con valore 3 it = v.erase(it); // erase restituisce l'iteratore al prossimo elemento --it; // se necessario, correggere l'iteratore } }

Domanda ingannevole

Domanda: Quando si chiama std::vector::insert, gli iteratori vengono invalidati?
Risposta comune: No, solo quando si aggiunge al di fuori dell'intervallo finale.
Risposta corretta: Tutti gli iteratori e i riferimenti uguali o successivi alla posizione di inserimento vengono invalidati se la capacità del contenitore aumenta. Se invece c'è abbastanza capacità — vengono invalidati solo gli iteratori nell'intervallo dopo il punto di inserimento.

Esempio:

std::vector<int> v = {1,2,3}; auto it = v.begin() + 1; v.insert(v.begin(), 0); // it qui potrebbe essere invalidato!

Esempi di errori reali a causa della mancanza di conoscenza delle sfumature del tema


Storia: In un progetto, per iterare su std::vector sono stati usati puntatori, e dopo push_back l'iterazione continuava su puntatori invalidati, causando il crash dell'applicazione.



Storia: Un sviluppatore ha rimosso elementi da std::list usando erase in un ciclo for(auto it : list), senza utilizzare l'iteratore restituito da erase, con conseguenza che l'iterazione saltava elemente e non venivano rimossi tutti i necessari.



Storia: Nel codice è stato utilizzato std::map, e dopo erase per chiave l'iteratore rimaneva legato all'elemento rimosso (comportamento indefinito) — ciò ha portato a crash casuali in ulteriori accessi.