Odpowiedzi:
const_iterator
s nie pozwalają na zmianę wartości, na które wskazują, zwykłe iterator
s to robią.
Podobnie jak w przypadku wszystkich rzeczy w C ++, zawsze preferuj const
, chyba że istnieje dobry powód, aby używać zwykłych iteratorów (tj. Chcesz wykorzystać fakt, że nie const
zmieniają one wskazanej wartości).
Powinny być oczywiste. Jeśli iterator wskazuje na element typu T, to const_iterator wskazuje na element typu „const T”.
Zasadniczo jest to odpowiednik typów wskaźników:
T* // A non-const iterator to a non-const element. Corresponds to std::vector<T>::iterator
T* const // A const iterator to a non-const element. Corresponds to const std::vector<T>::iterator
const T* // A non-const iterator to a const element. Corresponds to std::vector<T>::const_iterator
Iterator const zawsze wskazuje na ten sam element, więc sam iterator to const. Ale element, na który wskazuje, nie musi być const, więc element, na który wskazuje, można zmienić. Const_iterator to iterator wskazujący na element const, więc chociaż sam iterator może być aktualizowany (na przykład zwiększany lub zmniejszany), element, na który wskazuje, nie może zostać zmieniony.
const iterater
a const_iterator
.
Niestety, wiele metod dla kontenerów STL przyjmuje jako parametry iteratory zamiast const_iterators . Więc jeśli masz const_iterator , nie możesz powiedzieć „wstaw element przed elementem, na który wskazuje ten iterator” (mówienie takiej rzeczy nie jest koncepcyjnie naruszeniem const, moim zdaniem). Jeśli mimo wszystko chcesz to zrobić, musisz przekonwertować go na iterator inny niż const za pomocą std :: advanced () lub boost :: next () . Na przykład. boost :: next (container.begin (), std :: distance (container.begin (), the_const_iterator_we_want_to_unconst)) . Jeśli kontener jest std :: list , to czas wykonywania tego wywołania będzie wynosił O (n) .
Tak więc uniwersalna zasada dodawania const wszędzie tam, gdzie jest to „logiczne”, jest mniej uniwersalna, jeśli chodzi o kontenery STL.
Jednak kontenery boost przyjmują const_iterators (np. Boost :: unordered_map :: erase ()). Więc kiedy używasz pojemników przyspieszających, możesz być „agresywny”. Swoją drogą, czy ktoś wie, czy lub kiedy kontenery STL zostaną naprawione?
vector
i deque
wstawienie jednego elementu unieważnia wszystkie istniejące iteratory, co nie jest bardzo const
. Ale rozumiem twój punkt widzenia. Takie operacje są chronione przez kontenerowość const
, a nie iteratory. Zastanawiam się, dlaczego w standardowym interfejsie kontenera nie ma funkcji konwersji iteratora ze stałej na niekonstentną.
int const * foo;
int * const foo;
i int const * const foo;
wszystkie trzy są ważne i użyteczne, każdy na swój sposób. std::vector<int> const bar
powinien być taki sam jak drugi, ale niestety często jest traktowany jak trzeci. Podstawową przyczyną problemu jest to, że nie możemy powiedzieć, std::vector<int const> bar;
kiedy oznacza, że nie ma sposobu, aby uzyskać taki sam efekt, jak int const *foo;
w wektorze.
Minimalne przykłady do uruchomienia
Iteratory inne niż const pozwalają modyfikować to, na co wskazują:
std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();
*it = 1;
assert(v[0] == 1);
Iteratory stałych nie:
const std::vector<int> v{0};
std::vector<int>::const_iterator cit = v.begin();
// Compile time error: cannot modify container with const_iterator.
//*cit = 1;
Jak pokazano powyżej, v.begin()
jest const
przeciążony i zwraca albo iterator
lub w const_iterator
zależności od stałej zmiennej kontenera:
Typowym przypadkiem const_iterator
wyskakującego okienka this
jest użycie wewnątrz const
metody:
class C {
public:
std::vector<int> v;
void f() const {
std::vector<int>::const_iterator it = this->v.begin();
}
void g(std::vector<int>::const_iterator& it) {}
};
const
tworzy this
const, co tworzy this->v
const.
Zwykle możesz o tym zapomnieć za pomocą auto
, ale jeśli zaczniesz przekazywać te iteratory, będziesz musiał pomyśleć o nich przy sygnaturach metod.
Podobnie jak const i non-const, możesz łatwo konwertować z non-const na const, ale nie na odwrót:
std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();
// non-const to const.
std::vector<int>::const_iterator cit = it;
// Compile time error: cannot modify container with const_iterator.
//*cit = 1;
// Compile time error: no conversion from const to no-const.
//it = ci1;
Którego użyć: analogicznie do const int
vs int
: preferuj iteratory const, kiedy tylko możesz ich użyć (kiedy nie musisz modyfikować za ich pomocą kontenera), aby lepiej udokumentować zamiar czytania bez modyfikowania.
(jak powiedzieli inni) const_iterator nie pozwala na modyfikowanie elementów, na które wskazuje, jest to przydatne w metodach klasy const. Pozwala także wyrazić swoje zamiary.
ok Pozwólcie, że wyjaśnię to najpierw na bardzo prostym przykładzie bez użycia stałego iteratora weź pod uwagę, że mamy zbiór losowych liczb całkowitych zbiór "randomData"
for(vector<int>::iterator i = randomData.begin() ; i != randomData.end() ; ++i)*i = 0;
for(vector<int>::const_iterator i = randomData.begin() ; i!= randomData.end() ; ++i)cout << *i;
Jak widać do zapisu / edycji danych wewnątrz kolekcji używany jest normalny iterator, ale do odczytu użyto stałego iteratora. Jeśli spróbujesz użyć stałego iteratora w pierwszej pętli for, otrzymasz błąd. Zasadą jest używanie stałego iteratora do odczytu danych w kolekcji.