- •4.6. Иерархия итераторов stl: эффективная комбинация алгоритмов и контейнеров
- •4.7. Итераторы вставки
- •4.8. Еще раз о входе и выходе: потоковые итераторы
- •4.9. Спецификация категорий итераторов, требуемых алгоритмами stl
- •4.10. Разработка обобщенных алгоритмов
- •4.11. Почему некоторые алгоритмы требуют более мощные итераторы
- •4.12. Выбор правильного алгоритма
- •4.13. Константные и изменяемые итераторы
- •4.14. Категории итераторов, предоставляемые контейнерами stl
4.7. Итераторы вставки
Итератор вставки переводит обобщенный алгоритм в "режим вставки", а не в "режим перезаписи". Это означает, что присваивание *i = ... приводит не к перезаписи объекта в позиции i, а к вставке его в эту позицию с применением функции-члена контейнера для вставки. Итераторы вставки особенно полезны при пересылке последовательности данных из входного потока или контейнера в другой контейнер, когда длина последовательности заранее неизвестна. STL предоставляет три типа итераторов вставки, каждый из которых параметризован типом Container:
• back_insert_iterator<Container> — использует функцию-член push_back
класса Container;
• front_insert_iterator<Container> — использует функцию-член push_front
класса Container;
• insert_iterator<Container> — использует функцию-член insert класса
Container.
Такие вставки заставляют контейнер увеличивать выделенную память, в отличие от присваиваний, при которых в контейнере должно быть зарезервировано достаточное количество памяти. Например, следующий код некорректен, так как в vector1, куда выполняется копирование, недостаточно места для хранения данных:
vector<int> vector1; // vectorl пуст
deque<int> deque1(200, 1); // В dequel хранится 200 единиц.
copy(deque1.begin(), deque1.end(),
vector1.begin()); // ОШИБКА!
В процессе копирования возникает ошибка времени выполнения в инструкции
*(vector1.begin()) = *(deque1.begin())
Однако при использовании в качестве третьего аргумента итератора вставки алгоритм сору работает корректно, поскольку функция push_back вектора vector1 выполняет при необходимости перераспределение памяти:
copy(deque1.begin(), deque1.end(),
back_insert_iterator< vector<int> >(vector1));
Здесь back_insert_iterator<vector<int> > (vector1) вызывает конструктор
шаблона класса
template <typename Container>
class back_insert_iterator;
инстанцированный с vector<int> в качестве типа Container. Чтобы было удобнее
использовать итератор вставки, STL определяет шаблон обобщенной функции back_inserter.
template <typename Container>
inline back_insert_iterator<Container> // Возвращаемый тип
back_inserter(Container& x) {
return back_insert_iterator<Container>(x);
}
Таким образом, вызов copy можно записать более кратко:
copy(deque1.begin(), deque1.end(), back_inserter(vector1));
Компилятор автоматически инстанцирует функцию back_inserter с параметром типа Container, равным vector<int>. Инстанцированный экземпляр функции back_inserter вернет нам back_insert_iterator<Container>.
Итератор back_insert_iterator, а значит, и функция back_inserter, могут
использоваться с контейнерами vector, list и deque, поскольку все они имеют функцию-член push_back. Этот итератор вставки принимает любой контейнер С1, имеющий функцию-член push_back, и создает итератор с определенными в нем операторами * и = таким образом, что код
*i = х;
при выполнении делает вызов
C1.push_back(x);
Кроме того, итератор i, по сути, всегда указывает на конец связанного с ним контейнера, а операции ++I и i++ нe выполняют никаких действий.
Аналогично, front_insert_iterator и связанный с ним шаблон функции
front_inserter осуществляют вставку в начало контейнера. Они могут использоваться с контейнерами list и deque, поскольку эти контейнеры имеют функцию-член push_front:
int array1[100];
deque<int> deque1;
// Присваивание значений arrayl[0] , ..., arrayl[99]:
copy(&array1[0], &array1[100], front_inserter(deque1));
Элементы arrayl будут находиться в начале deque1 (в порядке, обратном порядку этих элементов в array1).
Контейнер vector функции-члена push_front не имеет, поскольку такая операция имела бы линейное время работы (то же относится и к функции pop_front, как вы увидите при рассмотрении адаптера очереди в разделе 9.2). Таким образом, front_inserter не может работать с контейнером vector.
Наиболее общим итератором вставки является insert_iterator, который имеет
соответствующий шаблон функции inserter и обеспечивает вставку в любое место текущей последовательности элементов контейнера, указываемое аргументом-итератором.
В этом случае элементы array1 будут вставлены после первого элемента deque1 и будут находиться в том же порядке, что и в array1 (т.е. после каждой вставки к итератору будет применяться операция инкремента ++, так что он будет перемещаться в позицию за только что вставленным элементом).
Итератор insert_iterator и функция inserter используют функцию-член
контейнера insert (iterator, value). Поскольку такая функция имеется у всех типов
контейнеров STL, inserter может применяться к контейнеру любого типа (включая отсортированные ассоциативные контейнеры).
Другие примеры использования итераторов вставки вы встретите в следующем разделе и в главах 5, "Обобщенные алгоритмы", и 12, "Программа для поиска в словаре".
