Const iterator c что это
Доброго времени суток.
Зачем нужен сonst_iterator в STL. Может потому что контейнер не может изменить данные под итератором?
Re: const_iterator
| От: | AlexanderVX | |
| Дата: | 06.06.12 00:18 | |
| Оценка: | 3 (1) +1 -1 | |
Здравствуйте, Nizumi, Вы писали:
N>Доброго времени суток.
N>Зачем нужен сonst_iterator в STL. Может потому что контейнер не может изменить данные под итератором?
IMHO для того, чтобы STL могла создавать итераторы, которые полностью предотвращают запись в объект по указателю итератора, а не просто с модификатором const C++. Современные OS позволяют накладывать атрибут read-only на регион памяти. То есть, получили const_iterator, и всё честно, гарантированно по нему нельзя писать, только перебирать. Особенно ценное свойство для разработки библиотек и компонентов для других разработчиков (когда доверие пользователю кода ограничено).
Re: const_iterator
| От: | Сыроежка | |
| Дата: | 06.06.12 00:58 | |
| Оценка: | 4 (2) +1 | |
Здравствуйте, Nizumi, Вы писали:
N>Доброго времени суток.
N>Зачем нужен сonst_iterator в STL. Может потому что контейнер не может изменить данные под итератором?
Чтобы можно было работать с константными контейнерами.
Представьте себе, что вы написали функцию, которая выводит на консоль содержимое контейнера. Очевидно, что вы этой функции будете передавать свой контейнер не по значению, а по ссылке, причем константной ссылке. Например, для вектора вы могли бы написать
template
void display( const std::vector &v );
Какой итератор вы можете использовать в теле этой функции? Очевидно, что только const_iterator, так как объект v является константным.
Меня можно встретить на www.cpp.forum24.ru
Re: const_iterator
| От: | sdf | ![]() |
| Дата: | 06.06.12 00:59 | |
| Оценка: |
Здравствуйте, Nizumi, Вы писали:
N>Доброго времени суток.
N>Зачем нужен сonst_iterator в STL. Может потому что контейнер не может изменить данные под итератором?
* если вам передали параметром константный указатель/ссылку на коллекцию.
* если у вас есть коллекция — член класса, а текущий выполняемый метод класса итерируется по ней и имеет модификатор const
В этих случаях использовать неконстантный итератор нельзя.

Re[2]: const_iterator
| От: | Сыроежка |
| Дата: | 06.06.12 01:03 |
| Оценка: |
Кстати сказать, это одна из распространеных ошибок программистов, когда они объявляют параметр как константную ссылку на контейнер, а затем внутри функции начинают писать что-то вроде следующее:
for ( typename std::vector::iterator it = v.begin(); it != v.end(); ++it )
< /*. */ >
Меня можно встретить на www.cpp.forum24.ru
Re[3]: const_iterator
| От: | dilmah |
| Дата: | 06.06.12 02:45 |
| Оценка: |
С>Кстати сказать, это одна из распространеных ошибок программистов, когда они объявляют параметр как константную ссылку на контейнер, а затем внутри функции начинают писать что-то вроде следующее:
С>for ( typename std::vector::iterator it = v.begin(); it != v.end(); ++it )
С> < /*. */ >
немножко не понял..
это же не скомпилируется, так?
Значит, этого не может быть в чужом коде, который ты смотрел, так?
Откуда же ты тогда знаешь, что это распространенная ошибка?
Или есть компиляторы, которые это компилируют?
Re: const_iterator
| От: | Vain | google.ru |
| Дата: | 06.06.12 02:47 | |
| Оценка: |
Здравствуйте, Nizumi, Вы писали:
N>Доброго времени суток.
N>Зачем нужен сonst_iterator в STL. Может потому что контейнер не может изменить данные под итератором?
Итератор обычно реализован через указатель на данные, а в стл если контейнер стал или был константным, то не только его нельзя изменять (добавлять/удалять элементы), но ещё нельзя и сами данные изменять. Следовательно, нужен новый тип итератора.
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[2]: const_iterator
| От: | johny5 | https://www.facebook.com/TachyonWars/ |
| Дата: | 06.06.12 04:24 | |
| Оценка: |
Здравствуйте, Сыроежка, Вы писали:
С>template
С>void display( const std::vector &v );
С>Какой итератор вы можете использовать в теле этой функции? Очевидно, что только const_iterator, так как объект v является константным.
Можно теперь подумать над вопросом, почему мы завели цельный новый тип вместо использования
typedef iteratorconst T> const_iterator;
Re[3]: const_iterator
| От: | sdf | ![]() |
| Дата: | 06.06.12 04:59 | |
| Оценка: |
Здравствуйте, johny5, Вы писали:
J>Здравствуйте, Сыроежка, Вы писали:
С>>template
С>>void display( const std::vector &v );
С>>Какой итератор вы можете использовать в теле этой функции? Очевидно, что только const_iterator, так как объект v является константным.
J>Можно теперь подумать над вопросом, почему мы завели цельный новый тип вместо использования
J>
J>typedef iteratorconst T> const_iterator; J>
Потому что тип итератора определяется не типом элемента контейнера, а типом самого контейнера (итератору нужен доступ к готовому типу контейнера)
в итогде получается как-то так:
vectorclass T> < typedef _vector_iterator_impl < vector> iterator; typedef _const_vector_iterator_impl < vector> const_iterator; >
на практике iterator может являтьсянаследником const_iterator
Функции
Увеличивает итератор на указанное количество позиций.
template void advance(InputIterator& InIt, Distance Off);
Параметры
InIt
Итератор, который должен увеличен, должен удовлетворять требованиям для итератора ввода.
Off
Целочисленный тип, который можно преобразовать в тип отличия итератора, указывающий число приращений позиции итератора, необходимо сдвинуть вперед.
Замечания
Диапазон должен быть неуклюжаемым, где итераторы должны быть разыменовыванием или в конце.
Если требования InputIterator к двунаправленному итератору соответствуют требованиям, может Off быть отрицательным. Если InputIterator входной или переадресационный итератор является типом, Off он должен быть ненагрегативным.
Расширенная функция имеет постоянную сложность при InputIterator удовлетворении требований к итератору случайного доступа; в противном случае она имеет линейную сложность и поэтому потенциально дорого.
Пример
// iterator_advance.cpp // compile with: /EHsc #include #include #include int main() < using namespace std; listL; for (int i = 1; i < 9; ++i) < L.push_back(i); >list::iterator LPOS = L.begin(); cout cout
The list L is: ( 1 2 3 4 5 6 7 8 ). The iterator LPOS initially points to the first element: 1. LPOS is advanced 4 steps forward to point to the fifth element: 5. LPOS is moved 3 steps back to point to the 2nd element: 2.
back_inserter
Создает итератор, может вставлять элементы с обратной стороны указанного контейнера.
template back_insert_iterator back_inserter(Container& Cont);
Параметры
Cont
Контейнер, в который будет выполняться вставка с обратной стороны.
Возвращаемое значение
back_insert_iterator , связанный с объектом-контейнером Cont .
Замечания
В стандартной библиотеке C++ аргумент должен ссылаться на один из трех контейнеров последовательности, имеющих функцию-член push_back : deque класс, list класс или vector класс.
Пример
// iterator_back_inserter.cpp // compile with: /EHsc #include #include #include int main() < using namespace std; vectorvec; for (int i = 0; i < 3; ++i) < vec.push_back(i); >cout cout << ")." << endl; // Insertions can be done with template function back_insert_iteratorbackiter(vec); *backiter = 30; backiter++; *backiter = 40; // Alternatively, insertions can be done with the // back_insert_iterator member function back_inserter(vec) = 500; back_inserter(vec) = 600; cout cout
The initial vector vec is: ( 0 1 2 ). After the insertions, the vector vec is: ( 0 1 2 30 40 500 600 ).
begin
Извлекает итератор для первого элемента в указанном контейнере.
template auto begin(Container& cont) ` -> decltype(cont.begin()); template auto begin(const Container& cont) ` -> decltype(cont.begin()); template Ty *begin(Ty (& array)[Size]);
Параметры
array
Массив объектов перечисления Ty .
Возвращаемое значение
Первые две функции шаблона возвращают cont.begin() . Первая функция не является константой; вторая является константой.
Третья функция шаблона возвращает array .
Пример
Рекомендуется использовать эту же функцию шаблона вместо члена контейнера begin() , если требуется более стандартное поведение.
// cl.exe /EHsc /nologo /W4 /MTd #include #include #include #include #include template void reverse_sort(C& c) < std::sort(std::begin(c), std::end(c), std::greater<>()); > template void print(const C& c) < for (const auto& e : c) < std::cout std::cout int main() < std::vectorv = < 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, 16, 8, 4, 2, 1 >; print(v); reverse_sort(v); print(v); std::cout ; print(arr); reverse_sort(arr); print(arr); >
11 34 17 52 26 13 40 20 10 5 16 8 4 2 1 52 40 34 26 20 17 16 13 11 10 8 5 4 2 1 -- 23 70 35 106 53 160 80 40 20 10 5 16 8 4 2 1 160 106 80 70 53 40 35 23 20 16 10 8 5 4 2 1
Функция reverse_sort поддерживает контейнеры любого типа, кроме обычных массивов, поскольку она обращается к версиям begin() , не являющимся членами. Кодирование reverse_sort для использования члена begin() контейнера:
template void reverse_sort(C& c) < using std::begin; using std::end; std::sort(c.begin(), c.end(), std::greater<>()); >
Затем отправка массива в него вызывает эту ошибку компилятора:
error C2228: left of '.begin' must have class/struct/union
cbegin
Извлекает итератор const (только для чтения) в первый элемент в указанном контейнере.
template auto cbegin(const Container& cont) -> decltype(cont.begin());
Параметры
cont
Контейнер или initializer_list .
Возвращаемое значение
Замечания
Эта функция работает со всеми контейнерами стандартной библиотеки C++ и с initializer_list .
Эту функцию-член можно использовать вместо функции шаблона begin() , чтобы гарантировать, что возвращаемое значение будет const_iterator . Как правило, он используется с ключевое слово вычета auto типов, как показано в следующем примере. В этом примере предположим, что Container является изменяемым контейнером (не const ) или initializer_list любого типа, который поддерживает begin() и cbegin() .
auto i1 = Container.begin(); // i1 is Container::iterator auto i2 = Container.cbegin(); // i2 is Container::const_iterator
cend
Извлекает итератор const (только для чтения) в элемент, который следует последнему элементу в указанном контейнере.
template auto cend(const Container& cont) -> decltype(cont.end());
Параметры
cont
Контейнер или initializer_list .
Возвращаемое значение
Замечания
Эта функция работает со всеми контейнерами стандартной библиотеки C++ и с initializer_list .
Эту функцию-член можно использовать вместо функции шаблона end() , чтобы гарантировать, что возвращаемое значение будет const_iterator . Как правило, он используется с ключевое слово вычета auto типов, как показано в следующем примере. В этом примере предположим, что Container является изменяемым контейнером (не const ) или initializer_list любого типа, который поддерживает end() и cend() .
auto i1 = Container.end(); // i1 is Container::iterator auto i2 = Container.cend(); // i2 is Container::const_iterator
crbegin
Получите обратный итератор только для чтения для элементов контейнера, начиная с конца контейнера.
template constexpr auto crbegin(const C& c) -> decltype(std::rbegin(c));
Параметры
c
Экземпляр контейнера.
Возвращаемое значение
Этот итератор возвращает элементы контейнера в обратном порядке, начиная с конца контейнера.

Пример: crbegin
#include #include int main() < std::vectorv; for (auto i = std::crbegin(v); i != std::crend(v); ++i) < std::cout // v[1] = 100; // error because the iterator is const >
30 20 10
crend
Получите sentinel в конце обратной последовательности элементов только для чтения.
template constexpr auto crend(const C& c) -> decltype(std::rend(c));
Параметры
c
Экземпляр контейнера.
Возвращаемое значение
Sentinel следует последнему элементу в обратном представлении контейнера.

crend Примере
#include #include int main() < std::vectorv; auto vi = std::crend(v); --vi; // get off the sentinel and onto the last element in the reversed range std::cout
data
Получите указатель на первый элемент в контейнере.
1) template constexpr auto data(C& c) -> decltype(c.data()); 2) template constexpr auto data(const C& c) -> decltype(c.data()); 3) template constexpr T* data(T (&array)[N]) noexcept; 4) template constexpr const E* data(initializer_list il) noexcept;
Параметры
c
Экземпляр контейнера.
E
Тип элемента списка инициализатора.
il
Список инициализатора.
N
Количество элементов в массиве.
T
Тип данных в массиве.
Возвращаемое значение
1, 2) Указатель на основе типа контейнера на первый элемент. Например, если контейнер является вектором целых чисел, тип возвращаемого значения — это . int *
3) Указатель на первый элемент в виде массива.
4) Указатель на первый элемент списка инициализатора.
Пример: data .
#include #include int main() < std::vectorv< 10, 20, 30 >; std::string src< "a string" >; const char *charPtr = std::data(src); int* intPtr = std::data(v); std::cout
a string, 10
distance
Указывает количество приращений между позициями, которые адресуют два итератора.
template typename iterator_traits::difference_type distance(InputIterator first, InputIterator last);
Параметры
first
Первый итератор, для которого нужно определить расстояние до второго.
last
Второй итератор, для которого нужно определить расстояние от первого.
Возвращаемое значение
Число раз, которое должно увеличиваться до тех пор, first пока он не равен last .
Замечания
Функция расстояния имеет постоянную сложность при InputIterator удовлетворении требований к итератору случайного доступа; в противном случае она имеет линейную сложность и поэтому потенциально дорого.
Пример
// iterator_distance.cpp // compile with: /EHsc #include #include #include int main() < using namespace std; listL; for (int i = -1; i < 9; ++i) < L.push_back(2 * i); >list ::iterator L_Iter, LPOS = L.begin(); cout cout << ")." << endl; cout << "The iterator LPOS initially points to the first element: " << *LPOS << "." << endl; advance(LPOS, 7); cout << "LPOS is advanced 7 steps forward to point " << " to the eighth element: " << *LPOS << "." << endl; list::difference_type Ldiff; Ldiff = distance(L.begin(), LPOS); cout
The list L is: ( -2 0 2 4 6 8 10 12 14 16 ). The iterator LPOS initially points to the first element: -2. LPOS is advanced 7 steps forward to point to the eighth element: 12. The distance from L.begin( ) to LPOS is: 7.
empty
template constexpr auto empty(const C& c) -> decltype(c.empty()); template constexpr bool empty(const T (&array)[N]) noexcept; template constexpr bool empty(initializer_list il) noexcept;
Параметры
c
Экземпляр контейнера.
E
Тип элемента списка инициализатора.
il
Список инициализатора.
N
Количество элементов в массиве.
T
Тип данных в массиве.
Возвращаемое значение
Возвращает значение true , если контейнер не имеет элементов; в противном случае false.
Пример
#include #include int main() < std::vectorv< 10,20,30 >; std::vector v2; std::cout
false, true
end
Извлекает итератор для элемента, следующего за последним элементом в указанном контейнере.
template auto end(Container& cont) -> decltype(cont.end()); template auto end(const Container& cont) -> decltype(cont.end()); template Ty *end(Ty (& array)[Size]);
Параметры
array
Массив объектов перечисления Ty .
Возвращаемое значение
Первые две функции шаблона возвращают cont.end() (первая не является константой, вторая является константой).
Третья функция шаблона возвращает array + Size .
Замечания
Пример кода см. в разделе begin .
front_inserter
Создает итератор, может вставлять элементы с передней стороны указанного контейнера.
template front_insert_iterator front_inserter(Container& Cont);
Параметры
Cont
Объект-контейнер, для которого выполняется вставка элемента в переднюю часть.
Возвращаемое значение
front_insert_iterator , связанный с объектом-контейнером Cont .
Замечания
Функция-член front_insert_iterator класса front_insert_iterator также может использоваться.
В стандартной библиотеке C++ аргумент должен ссылаться на один из двух контейнеров последовательности, которые имеют функцию-член push_back : класс deque или "класс list".
Пример
// iterator_front_inserter.cpp // compile with: /EHsc #include #include #include int main() < using namespace std; listL; for (int i = -1; i < 9; ++i) < L.push_back(i); >cout cout << ")." << endl; // Using the template function to insert an element front_insert_iteratorIter(L); *Iter = 100; // Alternatively, you may use the front_insert member function front_inserter(L) = 200; cout cout
The list L is: ( -1 0 1 2 3 4 5 6 7 8 ). After the front insertions, the list L is: ( 200 100 -1 0 1 2 3 4 5 6 7 8 ).
inserter
Вспомогающая функция шаблона, которая позволяет использовать inserter(Cont, Where) вместо insert_iterator(Cont, Where) него.
template insert_iterator inserter( Container& Cont, typename Container::iterator Where);
Параметры
Cont
Контейнер, в который будут добавляться новые элементы.
Where
Итератор, обнаруживающий точку вставки.
Замечания
Функция-шаблон возвращает insert_iterator (Cont, Where) .
Пример
// iterator_inserter.cpp // compile with: /EHsc #include #include #include int main() < using namespace std; listL; for (int i = 2; i < 5; ++i) < L.push_back(10 * i); >cout cout << ")." << endl; // Using the template version to insert an element insert_iteratorIter(L, L.begin()); *Iter = 1; // Alternatively, using the member function to insert an element inserter(L, L.end()) = 500; cout cout
The list L is: ( 20 30 40 ). After the insertions, the list L is: ( 1 20 30 40 500 ).
make_checked_array_iterator
Создает checked_array_iterator, который может использоваться другими алгоритмами.
Эта функция — расширение Майкрософт для стандартной библиотеки C++. Код, реализованный с помощью этой функции, нельзя перенести в стандартные среды сборки C, не поддерживающие это расширение Microsoft.
template checked_array_iterator make_checked_array_iterator( Iter Ptr, size_t Size, size_t Index = 0);
Параметры
Ptr
Указатель на массив назначения.
Size
Размер массива назначения.
Index
Необязательный индекс массива.
Возвращаемое значение
Замечания
Функция make_checked_array_iterator задается в пространстве имен stdext .
Эта функция принимает необработанный указатель, который в обычном случае вызывает проблемы, связанные с выходом за границы, и помещает его в оболочку класса checked_array_iterator, который выполняет проверку. Так как этот класс помечен как проверенный, стандартная библиотека С++ не выдает предупреждение о нем. Дополнительные сведения и примеры кода см. в разделе Проверенные итераторы.
Пример
В следующем примере создается и заполняется 10 элементами вектор. Содержимое вектора копируется в массив с помощью алгоритма копирования, а затем make_checked_array_iterator используется для указания назначения. Затем осуществляется проверка на преднамеренное нарушение границ для активации сбоя подтверждения отладки.
// make_checked_array_iterator.cpp // compile with: /EHsc /W4 /MTd #include #include // stdext::make_checked_array_iterator #include // std::make_unique #include #include #include using namespace std; template void print(const string& s, const C& c) < cout cout int main() < const size_t dest_size = 10; // Old-school but not exception safe, favor make_unique// int* dest = new int[dest_size]; unique_ptr updest = make_unique(dest_size); int* dest = updest.get(); // get a raw pointer for the demo vector v; for (int i = 0; i < dest_size; ++i) < v.push_back(i); >print("vector v: ", v); copy(v.begin(), v.end(), stdext::make_checked_array_iterator(dest, dest_size)); cout cout
make_move_iterator
Создает move iterator , содержащий предоставленный итератор в качестве итератора stored .
template move_iterator make_move_iterator(const Iterator& It);
Параметры
It
Итератор, хранящийся в новом итераторе перемещения.
Замечания
Функция-шаблон возвращает move_iterator (_It) .
make_unchecked_array_iterator
Создает unchecked_array_iterator, который может использоваться другими алгоритмами.
Эта функция — расширение Майкрософт для стандартной библиотеки C++. Код, реализованный с помощью этой функции, нельзя перенести в стандартные среды сборки C, не поддерживающие это расширение Microsoft.
template unchecked_array_iterator make_unchecked_array_iterator(Iter Ptr);
Параметры
Ptr
Указатель на массив назначения.
Возвращаемое значение
Замечания
Функция make_unchecked_array_iterator задается в пространстве имен stdext .
Эта функция принимает необработанный указатель и помещает его в класс, который не выполняет проверки, и поэтому оптимизирует к минимуму, но также подавляет предупреждения компилятора, например C4996. Соответственно, это целевой способ работы с предупреждениями непроверенных указателей без их глобального подавления или возникновения стоимости проверки. Дополнительные сведения и примеры кода см. в разделе Проверенные итераторы.
Пример
В следующем примере создается и заполняется 10 элементами вектор. Содержимое вектора копируется в массив с помощью алгоритма копирования, а затем make_unchecked_array_iterator используется для указания назначения.
// make_unchecked_array_iterator.cpp // compile with: /EHsc /W4 /MTd #include #include // stdext::make_unchecked_array_iterator #include #include #include using namespace std; template void print(const string& s, const C& c) < cout cout int main() < const size_t dest_size = 10; int* dest = new int[dest_size]; vectorv; for (int i = 0; i < dest_size; ++i) < v.push_back(i); >print("vector v: ", v); // COMPILER WARNING SILENCED: stdext::unchecked_array_iterator is marked as checked in debug mode // (it performs no checking, so an overrun will trigger undefined behavior) copy(v.begin(), v.end(), stdext::make_unchecked_array_iterator(dest)); cout cout
vector v: 0 1 2 3 4 5 6 7 8 9 int array dest: 0 1 2 3 4 5 6 7 8 9
next
Выполняет итерацию заданное число раз и возвращает новую позицию итератора.
template InputIterator next( InputIterator first, typename iterator_traits::difference_type off = 1);
Параметры
first
Текущая позиция.
off
Количество раз для прохода.
Возвращаемое значение
Возвращает новую позицию итератора после прохода off раз.
Замечания
Функция-шаблон возвращает next , приращенный off раз.
prev
Выполняет обратную итерацию заданное число раз и возвращает новую позицию итератора.
template BidirectionalIterator prev( BidirectionalIterator first, typename iterator_traits::difference_type off = 1);
Параметры
first
Текущая позиция.
off
Количество раз для прохода.
Замечания
Функция-шаблон возвращает next , уменьшенный off раз.
rbegin
Получите итератор, который возвращает элементы контейнера в обратном порядке.
template constexpr auto rbegin(C& c) -> decltype(c.rbegin()); template constexpr auto rbegin(const C& c) -> decltype(c.rbegin());
Параметры
c
Экземпляр контейнера.
Возвращаемое значение
Возвращаемый итератор представляет элементы контейнера в обратном порядке, начиная с конца обратного диапазона.

Пример: rbegin .
#include #include int main() < std::vectorv< 10, 20, 30 >; for (auto e = std::rbegin(v); e != std::rend(v); ++e) < std::cout >
30 20 10
rend
Получите sentinel в конце обратной последовательности элементов.
template constexpr auto rend(C& c)-> decltype(c.rend()); template constexpr auto rend(const C& c) -> decltype(c.rend());
Параметры
c
Экземпляр контейнера.
Возвращаемое значение
Обратный итератор в sentinel в конце контейнера. Sentinel следует последнему элементу в обратном представлении контейнера:

rend Примере
#include #include int main() < std::vectorv; auto vi = std::rend(v); --vi; // get off the sentinel and onto the last element in the reversed range std::cout
size
template constexpr auto size(const C& c) -> decltype(c.size()); template constexpr size_t size(const T (&array)[N]) noexcept;
Параметры
c
Экземпляр контейнера.
N
Количество элементов в массиве.
T
Тип данных в массиве.
Возвращаемое значение
Количество элементов в контейнере в виде целого числа без знака.
Пример: size .
#include #include int main() < std::vectorv< 10, 20, 30 >; size_t s = std::size(v); std::cout
В чем разница между const_iterator и non-const итератором в C++ STL?
const_iterator s не позволяют вам изменять значения, на которые они указывают, как это делают обычные iterator . Как и во всех случаях в C++, всегда предпочитайте const , если нет веских причин использовать обычные итераторы (т.е. вы хотите использовать тот факт, что они не являются const для изменения значения, на которое они указывают).
Поделиться 21 ноября 2008 в 17:39
Они должны быть в основном самообъяснимыми. Если итератор указывает на элемент типа T, то const_iterator указывает на элемент типа 'const T'. Это в основном эквивалентно типам указателей:
T* // A non-const iterator to a non-const element. Corresponds to std::vector::iterator T* const // A const iterator to a non-const element. Corresponds to const std::vector::iterator const T* // A non-const iterator to a const element. Corresponds to std::vector::const_iterator
Итератор const всегда указывает на один и тот же элемент, поэтому итератор сам является const. Но элемент, на который он указывает, не должен быть const, поэтому элемент, на который он указывает, может быть изменен. A const_iterator - это итератор, который указывает на элемент const, поэтому, хотя сам итератор может быть обновлен (увеличен или уменьшен, например), элемент, на который он указывает, не может быть изменен.
Поделиться 21 ноября 2008 в 17:51
Минимальные примеры запускаемых данных Неконстантные итераторы позволяют изменять то, что они указывают:
std::vector v; std::vector::iterator it = v.begin(); *it = 1; assert(v[0] == 1);
Неконстантные итераторы:
const std::vector v; std::vector::const_iterator cit = v.begin(); // Compile time error: cannot modify container with const_iterator. //*cit = 1;
- Как begin() знает, какой тип возвращения должен быть возвращен (константный или неконстантный)?
- Как работает перегрузка константных и неконстантных функций?
Обычный случай, когда появляется const_iterator , когда this используется внутри метода const :
class C < public: std::vectorv; void f() const < std::vector::const_iterator it = this->v.begin(); > void g(std::vector::const_iterator& it) <> >;
const делает this const, что делает this->v const.
Обычно вы можете забыть об этом с помощью auto , но если вы начинаете передавать эти итераторы, вам нужно будет подумать о них для сигнатур метода.
В большинстве случаев, как и в случае с const и non-const, вы можете легко преобразовать из non-const в const, но не наоборот:
std::vector v; std::vector::iterator it = v.begin(); // non-const to const. std::vector::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;
Какой из них использовать: аналогично const int или int : предпочитайте его констераторы, когда вы можете использовать их (когда вам не нужно изменять контейнер с ними), чтобы лучше документировать ваше намерение читать без изменения.
Поделиться 22 декабря 2016 в 02:55
К сожалению, многие методы для контейнеров STL принимают итераторы вместо const_iterators в качестве параметров. Поэтому, если у вас есть const_iterator,, вы не можете сказать "вставить элемент перед элементом, на который указывает этот итератор" (на мой взгляд, это не является концептуальным нарушением const). Если вы хотите сделать это в любом случае, вам нужно преобразовать его в неconst итератор, используя std::advance() или boost::next()., например, boost::next(container.begin(), std::distance(container.begin(), the_const_iterator_we_want_to_unconst)). Если container является std::list, то время выполнения этого вызова будет O(n).
Таким образом, универсальное правило добавлять const там, где это "логично", является менее универсальным, когда речь идет о контейнерах STL.
Однако, контейнеры поднятия принимают конст_итераторы (например, boost::unordered_map::erase()).) Таким образом, когда вы используете контейнеры поднятия, вы можете быть "конст-агрессивными". Кстати, кто-нибудь знает, будут ли контейнеры STL исправляться или когда?
Поделиться 02 сентября 2010 в 16:18
Используйте const_iterator когда только сможете, используйте итератор, когда у вас нет другого выбора.
Поделиться 21 ноября 2008 в 17:51
(как сказали другие) const_iterator не позволяет вам изменять элементы, на которые он указывает, это полезно внутри методов класса const. Он также позволяет вам выражать свое намерение.
Поделиться 21 ноября 2008 в 18:42
Хорошо, позвольте мне объяснить это очень простым примером без использования постоянного итератора, учитывая, что у нас есть коллекция случайных целых чисел "randomData"
for(vector::iterator i = randomData.begin() ; i != randomData.end() ; ++i)*i = 0; for(vector::const_iterator i = randomData.begin() ; i!= randomData.end() ; ++i)cout
Как видно, для записи/редактирования данных внутри коллекции используется обычный итератор, но для чтения с целью был использован постоянный итератор. Если вы попробуете использовать постоянный итератор в первом цикле for, вы получите ошибку. Как правило, используйте постоянный итератор для чтения данных внутри коллекции.
Const iterator c что это
Итераторы обеспечивают доступ к элементам контейнера и представляют реализацию распространенного паттерна объектно-ориентированного программирования "Iterator". С помощью итераторов очень удобно перебирать элементы. В C++ итераторы реализуют общий интерфейс для различных типов контейнеров, что позволяет использовать единой подход для обращения к элементам разных типов контейнеров.
Стоит отметить, что итераторы имеют только контейнеры, адаптеры контейнеров — типы std::stack , std::queue и std::priority_queue итераторов не имеют.
Итератор описывается типом iterator . Для каждого контейнера конкретный тип итератора будет отличаться. Так, итератор для контейнера list представляет тип list::iterator , а итератор контейнера vector представляет тип vector::iterator и так далее. Однако общий функционад, который применяется для доступа к элементам, будет аналогичен.
Для получения итераторов контейнеры в C++ обладают такими функциями, как begin() и end() . Функция begin() возвращает итератор, который указывает на первый элемент контейнера (при наличии в контейнере элементов). Функция end() возвращает итератор, который указывает на следующую позицию после последнего элемента, то есть по сути на конец контейнера. Если контейнер пуст, то итераторы, возвращаемые обоими методами begin и end совпадают. Если итератор begin не равен итератору end, то между ними есть как минимум один элемент.
Обе этих функции возвращают итератор для конкретного типа контейнера:
#include #include int main() < std::vectornumbers< 1,2,3,4 >; std::vector::iterator iter = numbers.begin(); // получаем итератор >
В данном случае создается вектор - контейнер типа vector, который содержит значения типа int. И этот контейнер инициализируется набором . И через метод begin() можно получить итератор для этого контейнера. Причем этот итератор будет указывать на первый элемент контейнера.
С итераторами можно проводить следующие операции:
- *iter : получение элемента, на который указывает итератор
- ++iter : перемещение итератора вперед для обращения к следующему элементу
- --iter : перемещение итератора назад для обращения к предыдущему элементу. Итераторы контейнера forward_list не поддерживают операцию декремента.
- iter1 == iter2 : два итератора равны, если они указывают на один и тот же элемент
- iter1 != iter2 : два итератора не равны, если они указывают на разные элементы
- iter + n : возвращает итератор, который смещен от итератора iter на n позиций вперед
- iter - n : возвращает итератор, который смещен от итератора iter на n позиций назад
- iter += n : перемещает итератор на n позиций вперед
- iter -= n : перемещает итератор на n позиций назад
- iter1 - iter2 : возвращает количество позиций между итераторами iter1 и iter2
- >, >=,
Стоит отметить, что итераторы не всех контейнеров поддерживают все эти операции.
Итераторы для типов std::forward_list , std::unordered_set и std::unordered_map не поддерживают операции --, -= и -. (поскольку std::forward_list - однонаправленный список, где каждый элемент хранит указатель только на следующий элемент)
Итераторы для типа std::list поддерживают операции инкремента и декремента, но не поддерживаются операции +=, -=, + и -. Те же ограничения имеют итераторы контейнеров std::map и std::set .
Операции +=, -=, +, -, , >= и поддерживаются только итераторами произвольного доступа (итераторы контейнеров std::vector , array и deque )
Получение и изменение элемента контейнера
Поскольку итератор по сути представляет указатель на определенный элемент, то через этот указатель мы можем получить текущий элемент итератора и изменить его значение:
#include #include int main() < std::vectornumbers< 1,2,3,4 >; auto iter < numbers.begin() >; // получаем итератор // получаем элемент, на который указывает итератор std::cout
После получения итератора он будет указывать на первый элемент контейнера. То есть при выражение *iter возвратит первый элемент вектора.
Прибавляя или отнимая определенное число, можно переместить итератор вперед или назад на определенное количество элементов:
#include #include int main() < std::vectornumbers< 10, 20, 30, 40 >; auto iter < numbers.begin() >; // получаем итератор // переходим на 1 элемент вперед ко 2-му элементу ++iter; std::cout
Опять же повторю, что стоит учитывать, что не все операции поддерживаются итераторами всех контейнеров.
Перебор контейнера
Например, используем итераторы для перебора элементов вектора:
#include #include int main() < std::vectornumbers< 10, 20, 30, 40 >; auto iter < numbers.begin() >; // получаем итератор while(iter!=numbers.end()) // пока не дойдем до конца < std::cout // аналогичный пример с циклом for for(auto start; start !=numbers.end(); start++ ) < std::cout >
При работе с контейнерами следует учитывать, что добавление или удаление элементов в контейнере может привести к тому, что все текущие итераторы для данного контейнера, а также ссылки и указатели на его элементы станут недопустимыми. Поэтому при добавлении или удалении элементов в контейнере в общем случае следует перестать использовать текущие итераторы для этого контейнера.
Константные итераторы
Если контейнер представляет константу, то для обращения к элементам этого контейнера можно использовать только константный итератор (тип const_iterator ). Такой итератор позволяет считывать элементы, но не изменять их:
const vector numbers; for(auto iter ; iter != numbers.end(); ++iter) < std::cout
В данном случае итератор iter будет представлять тип std::vector::const_iterator .
Для получения константного итератора также можно использовать функции cbegin() и cend . При этом даже если контейнер не представляет константу, но для его перебора используется константный итератор, то опять же нельзя изменять значения элементов этого контейнера:
#include #include int main() < std::vectornumbers < 1, 2, 3, 4, 5 >; for (auto iter ; iter != numbers.cend(); ++iter) < std::cout >
Стоит отметить, что для типов std::set (множество) и std::map (словарь) доступны только константные итераторы.
Реверсивные итераторы
Реверсивные итераторы позволяют перебирать элементы контейнера в обратном направлении. Для получения реверсивного итератора применяются функции rbegin() и rend() , а сам итератор представляет тип reverse_iterator :
#include #include int main() < std::vectornumbers < 1, 2, 3, 4, 5 >; for (auto iter ; iter != numbers.rend(); ++iter) < std::cout std::cout
В данном случае итератор будет представлять тип std::vector::reverse_iterator . Консольный вывод программы:
5 4 3 2 1
Если надо обеспечить защиту от изменения значений контейнера, то можно использовать константный реверсивный итератор, который представлен типом const_reverse_iterator и который можно получить с помощью функций crbegin() и crend() :
#include #include int main() < std::vectornumbers < 1, 2, 3, 4, 5 >; for (auto iter ; iter != numbers.crend(); ++iter) < std::cout >
Итераторы для массивов
Для массивов в C++ также имеется поддержка итераторов. Для этого в стандартной библиотеке С++ определены функции std::begin() (возвращает итератор на начало массива) и std::end() (возвращает итератор на конец массива):
int data[]; // получаем итератор на начало массива auto iter = std::begin(data); // получаем итератор на конец массива auto end = std::end(data);
Как и контейнеры, массив можно перебрать с помощью итераторов:
#include int main() < int data[]; // перебор массива с помощью итераторов for(auto iter ; iter != std::end(data); iter++) < std::cout >
Но перебор массива вполне можно сделать и другими способами - через индексы, обычные указатели. Но итераторы на массивы могут быть полезны при манипуляции с контейнерами. Например, функция insert() , которая есть у ряда контейнеров, позволяет добавить в контейнер какую-то часть другого контейнера. Для выделения добавляемой части могут применяться итераторы. И таким образом, с помощью итераторов можно добавить в контейнер, например, в вектор какую-то часть контейнера:
#include #include int main() < int data[]; std::vector numbers < 1, 2, 3, 4>; // добавляем в конец вектора numbers из массива data элементы со 2-го по предпоследний (включительно) numbers.insert(numbers.end(), std::begin(data) + 1, std::end(data)-1); for (auto iter ; iter != numbers.end(); ++iter) < std::cout std::cout
numbers.insert(numbers.end(), std::begin(data) + 1, std::end(data)-1);
Добавляет в вектор numbers, начиная с позиции, на которую указывает итератор numbers.end() (то есть в самый конец вектора), диапазон элементов массива data. Начало этого диапазона задается выражением std::begin(data) + 1 (то есть со 2-го элемента), а конуц - выражением std::end(data)-1 (то есть по предпоследний элемент включительно). Консольный вывод:
