Iteratory to obiekty, które pozwalają odwoływać się do elementów kontenerów STL podobnie jak wskaźniki. Oferują jednolity sposób dostępu do elementów dowolnych kontenerów (vector, list, map itp.). Istnieje kilka rodzajów iteratorów:
Szczególną uwagę należy zwrócić na życie iteratorów:
Przykład:
std::vector<int> v = {1,2,3,4,5}; for (auto it = v.begin(); it != v.end(); ++it) { if (*it == 3) { // Usuwamy element o wartości 3 it = v.erase(it); // erase zwraca iterator na następny element --it; // w razie potrzeby skorygować iterator } }
Pytanie: Przy wywołaniu std::vector::insert, czy iteratory stają się nieważne?
Częsty odpowiedź: Nie, tylko przy dodawaniu poza zakresem końca.
Poprawna odpowiedź: Wszystkie iteratory i odniesienia, które są równe lub następują za pozycją wstawienia, stają się nieważne, jeśli pojemność kontenera się zwiększa. Jeśli pojemność jest wystarczająca — nieważne stają się tylko iteratory w zasięgu po punkcie wstawienia.
Przykład:
std::vector<int> v = {1,2,3}; auto it = v.begin() + 1; v.insert(v.begin(), 0); // it tutaj może zostać unieważnione!
Historia: W projekcie do iteracji po std::vector używano wskaźników i po push_back iteracja kontynuowała się po unieważnionych wskaźnikach, co prowadziło do awarii aplikacji.
Historia: Programista usuwał elementy std::list za pomocą erase w pętli for(auto it : list), nie używając zwracanego iteratora erase, co spowodowało, że iteracja przeskakiwała przez elementy i nie wszystkie potrzebne zostały usunięte.
Historia: W kodzie używano std::map, a po erase według klucza iterator pozostawał powiązany z usuniętym elementem (niezdefiniowane zachowanie) — prowadziło to do przypadkowych awarii przy dalszych odwołaniach.