Итераторы
Итераторы обеспечивают доступ к элементам контейнера. С помощью итераторов очень удобно перебирать элементы. Итератор описывается типом iterator. Но для каждого контейнера конкретный тип итератора будет отличаться. Так, итератор для контейнера list<int> представляет тип list<int>::iterator, а итератор контейнера vector<int> представляет тип vector<int>::iterator и так далее.
Для получения итераторов контейнеры в C++ обладают такими функциями, как begin() и end(). Функция begin() возвращает итератор, который указывает на первый элемент контейнера (при наличии в контейнере элементов). Функция end() возвращает итератор, который указывает на следующую позицию после последнего элемента, то есть по сути на конец контейнера. Если контейнер пуст, то итераторы, возвращаемые обоими методами begin и end совпадают. Если итератор begin не равен итератору end, то между ними есть как минимум один элемент.
Обе этих функции возвращают итератор для конкретного типа контейнера:
1
2
std::vector<int> v = { 1,2,3,4 };
std::vector<int>::iterator iter = v.begin(); // получаем итератор
В данном случае создается вектор - контейнер типа vector, который содержит значения типа int. И этот контейнер инициализируется набором {1, 2, 3, 4}. И через метод begin() можно получить итератор для этого контейнера. Причем этот итератор будет указывать на первый элемент контейнера.
Операции с итераторами
С итераторами можно проводить следующие операции:
*iter: получение элемента, на который указывает итератор
++iter: перемещение итератора вперед для обращения к следующему элементу
--iter: перемещение итератора назад для обращения к предыдущему элементу. Итераторы контейнера forward_list не поддерживают операцию декремента.
iter1 == iter2: два итератора равны, если они указывают на один и тот же элемент
iter1 != iter2: два итератора не равны, если они указывают на разные элементы
Например, используем итераторы для перебора элементов вектора:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = {1, 2, 3, 4, 5};
auto iter = v.begin(); // получаем итератор
while(iter!=v.end()) // пока не дойдем до конца
{
std::cout << *iter << std::endl;// получаем элементы через итератор
++iter; // перемещаемся вперед на один элемент
}
return 0;
}
При работе с контейнерами следует учитывать, что добавление или удаление элементов в контейнере может привести к тому, что все текущие итераторы для данного контейнера, а также ссылки и указатели на его элементы станут недопустимыми.
Итераторы позволяют не только получать элементы, но и изменять их:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = {1, 2, 3, 4, 5};
auto iter = v.begin();
while(iter!=v.end()) // пока не дойдем до конца
{
*iter = (*iter) * (*iter); // возводим число в квадрат
++iter;
}
for(iter = v.begin(); iter!=v.end(); ++iter)
{
std::cout << *iter << std::endl;
}
return 0;
}
В данном случае в цикле while элементы вектора возводятся в квадрат. Консольный вывод данной программы:
1
4
9
16
25
Константные итераторы
Если контейнер представляет константу, то для обращения к элементам этого контейнера можно использовать только константный итератор (тип const_iterator). Такой итератор позволяет считывать элементы, но не изменять их:
1
2
3
4
5
6
7
const vector<int> v = {1, 2, 3, 4, 5};
for(auto iter = v.begin(); iter != v.end(); ++iter)
{
std::cout << *iter << std::endl;
// так нельзя сделать
//*iter = (*iter) * (*iter);
}
Для получения константного итератора также можно использовать функции cbegin() и cend. При этом даже если контейнер не представляет константу, но при этом для его перебора используется константный итератор, то опять же нельзя изменять значения элементов этого контейнера:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = { 1, 2, 3, 4, 5 };
for (std::vector<int>::const_iterator iter = v.cbegin(); iter != v.cend(); ++iter)
{
std::cout << *iter << std::endl;
// так нельзя сделать, так как итератор константный
//*iter = (*iter) * (*iter);
}
return 0;
}
Реверсивные итераторы
Реверсивные итераторы позволяют перебирать элементы контейнера в обратном направлении. Для получения реверсивного итератора применяются функции rbegin() и rend(), а сам итератор представляет тип :
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = { 1, 2, 3, 4, 5 };
for (std::vector<int>::reverse_iterator iter = v.rbegin(); iter != v.rend(); ++iter)
{
std::cout << *iter << std::endl;
}
std::cout << "\n";
return 0;
}
Консольный вывод программы:
5
4
3
2
1
Если надо обеспечить защиту от изменения значений контейнера, то можно использовать константный реверсивный итератор, который представлен типом const_reverse_iterator и который можно получить с помощью функций crbegin() и crend():
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = { 1, 2, 3, 4, 5 };
for (std::vector<int>::const_reverse_iterator iter = v.crbegin(); iter != v.crend(); ++iter)
{
std::cout << *iter << std::endl;
// так нельзя сделать, так как итератор константный
//*iter = (*iter) * (*iter);
}
return 0;
}
Также итераторы для всех типов, кроме list и forward_list, поддерживают ряд дополнительных операций:
iter + n: возвращает итератор, который смещен от итератора iter на n позиций вперед
iter - n: возвращает итератор, который смещен от итератора iter на n позиций назад
iter += n: перемещает итератор на n позиций вперед
iter -= n: перемещает итератор на n позиций назад
iter1 - iter2: возвращает количество позиций между итераторами iter1 и iter2
>, >=, <, <=: операции сравнения. Один итератор больше другого, если указывает на элемент, который ближе к концу
Применение некоторых операций:
1
2
3
4
5
6
7
std::vector<int> v = {1, 2, 3, 4, 5};
auto iter1 = v.begin();
auto iter2 = iter1 + 2;
std::cout << *iter2 << std::endl; // 3
bool res = iter2 > iter1; // true
std::cout << res << std::endl;