На этом шаге мы рассмотрим функции получения итераторов.
Отображения и мультиотображения не поддерживают прямой доступ к элементам, поэтому для обращения к элементам обычно используются итераторы.
Впрочем, у этого правила существует исключение: отображения поддерживают оператор индексирования [] для прямого обращения к элементам. В таблице 1 перечислены стандартные функции получения итераторов, поддерживаемые отображениями и мультиотображениями.
Операция | Описание |
---|---|
c.begin() | Возвращает двунаправленный итератор для первого элемента (ключи считаются константными) |
c.end() | Возвращает двунаправленный итератор для позиции за последним элементом (ключи считаются константными) |
c.rbegin() | Возвращает обратный итератор для первого элемента при переборе в обратном направлении |
c.rend() | Возвращает обратный итератор для позиции за последним элементом при переборе в обратном направлении |
Итераторы отображений и мультиотображений, как и во всех классах ассоциативных контейнеров, являются двунаправленными. Такие итераторы не могут использоваться алгоритмами, рассчитанными на итераторы произвольного доступа (например, алгоритмами сортировки или случайной перестановки элементов).
Но еще более важное ограничение заключается в том, что с точки зрения итератора все ключи элементов отображения или мультиотображения считаются константными (то есть элемент интерпретируется как относящийся к типу pair<const key,T>). Это необходимо для того, чтобы программа не могла нарушить упорядоченность элементов, изменяя их ключи. Однако в результате для элементов отображения или мультиотображения вызов модифицирующих алгоритмов становится невозможным. Например, удаление элементов не может осуществляться алгоритмом remove(), потому что "удаление" в действительности сводится к перезаписи следующими элементами (данная тема подробно обсуждается на 108 шаге). Элементы множеств и мультимножеств удаляются только функциями, предоставляемыми контейнером.
Пример использования итераторов:
std::map<std::string,float> coll;
std::map<std::string,float>::iterator pos;
for (pos = coll.begin(); pos != coll.end(); ++pos) {
std::cout << "key: " << pos->first << "\t"
<< "value: " << pos->second << std::endl;
}
Итератор pos используется для перебора в последовательности пар string/float. Выражение pos->flrst определяет ключ элемента, а выражение pos->second - значение элемента.
Попытка изменения ключа приводит к ошибке:
pos->first = "hello"; // ОШИБКА компипяции
Однако модификация значения элемента выполняется без проблем (при условии, что значение не принадлежит к константному типу):
pos->second = 13.5; // OK
Изменить ключ элемента можно только одним способом: заменить старый элемент новым элементом с тем же значением. Унифицированная функция для выполнения этой операции выглядит так:
namespace MyLib { template <class Cont> inline bool replace_key (Cont& c, const typename Cont::key_type& old_key, const typename Cont::key_type& new_key) { typename Cont::iterator pos; pos = c.find(old_key); if (pos != c.end()) { // Вставка нового элемента со значением старого элемента c.insert(typename Cont::value_type(new_key,pos->second)); // Удаление старого элемента c.erase(pos); return true; } else { // Ключ не найден return false; } } }
Функции insert() и erase() описаны в следующем шаге.
При вызове этой унифицированной функции контейнеру просто передаются два ключа: старый и новый. Пример:
std::map<std::string,float> coll; . . . . . MyLib::replace_key(coll,"old key","new key");
С мультиотображениями функция работает аналогично. Впрочем, отображения поддерживают более удобный способ модификации ключа элемента. Вместо вызова replace_key() достаточно написать следующее:
// Вставка нового элемента со значением старого элемента coll["new_key"] = coll["old_key"]; // Удаление старого элемента coll.erase("old_key");
На следующем шаге мы рассмотрим вставку и удаление элементов.