Выбрать главу

vector<int>::iterator it;        // it позволяет читать и записывать

                                 // в элементы вектора vector<int>

string::iterator it2;            // it2 позволяет читать и записывать

                                 // символы в строку

vector<int>::const_iterator it3; // it3 позволяет читать, но не

                                 // записывать элементы

string::const_iterator it4;      // it4 позволяет читать, но не

                                 // записывать символы

Тип const_iterator ведет себя как константный указатель (см. раздел 2.4.2). Как и константный указатель, тип const_iterator позволяет читать, но не писать в элемент, на который он указывает; объект типа iterator позволяет и читать, и записывать. Если вектор или строка являются константой, можно использовать итератор только типа const_iterator. Если вектор или строка на являются константой, можно использовать итератор и типа iterator, и типа const_iterator.

Терминология. Итераторы и типы итераторов

Термин итератор (iterator) используется для трех разных сущностей. Речь могла бы идти о концепции итератора, или о типе iterator, определенном классом контейнера, или об объекте итератора.

Следует уяснить, что существует целый набор типов, связанных концептуально. Тип относится к итераторам, если он поддерживает общепринятый набор функций. Эти функции позволяют обращаться к элементу в контейнере и переходить с одного элемента на другой.

Каждый класс контейнера определяет тип по имени iterator, который обеспечивает действия концептуального итератора.

Функции begin() и end()

Тип, возвращаемый функциями begin() и end(), зависит от константности объекта, для которого они были вызваны. Если объект является константой, то функции begin() и end() возвращают итератор типа const_iterator; если объект не константа, они возвращают итератор типа iterator.

vector<int> v;

const vector<int> cv;

auto it1 = v.begin();  // it1 имеет тип vector<int>::iterator

auto it2 = cv.begin(); // it2 имеет тип vector<int>::const_iterator

Зачастую это стандартное поведение желательно изменить. По причинам, рассматриваемым в разделе 6.2.3, обычно лучше использовать константный тип (такой как const_iterator), когда необходимо только читать, но не записывать в объект. Чтобы позволить специально задать тип const_iterator, новый стандарт вводит две новые функции, cbegin() и cend():

auto it3 = v.cbegin(); // it3 имеет тип vector<int>::const_iterator

Подобно функциям-членам begin() и end(), эти функции-члены возвращают итераторы на первый и следующий после последнего элементы контейнера. Но независимо от того, является ли вектор (или строка) константой, они возвращают итератор типа const_iterator.

Объединение обращения к значению и доступа к члену

При обращении к значению итератора получается объект, на который указывает итератор. Если этот объект имеет тип класса, то может понадобиться доступ к члену полученного объекта. Например, если есть вектор строк, то может понадобиться узнать, не пуст ли некий элемент. С учетом, что it — это итератор данного вектора, можно следующим образом проверить, не пуста ли строка, на которую он указывает:

(*it).empty()

По причинам, рассматриваемым в разделе 4.1.2, круглые скобки в части (*it).empty() необходимы. Круглые скобки требуют применить оператор обращения к значению к итератору it, а к результату применить точечный оператор (см. раздел 1.5.2). Без круглых скобок точечный оператор относился бы к итератору it, а не к полученному объекту.

(*it).empty() // обращение к значению it и вызов функции-члена empty()