Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
STL5 / lab4-iterators / lab4-iterators.doc
Скачиваний:
36
Добавлен:
10.04.2015
Размер:
299 Кб
Скачать

Итераторы потоков ввода-вывода

Библиотека STL позволяет обращаться с потоками файлового и консольного ввода-вывода (а также с любыми другими классами, производными от istream и ostream) как с контейнерами: для них существуют специальные итераторы istream_iterator и ostream_iterator. Рассмотрим пример: Нижеприведенная программа вводит с клавиатуры целые числа - коды символов - и печатает символы, соответствующие этим кодам.

istream_iterator<char> input(cin); // (1)

istream_iterator<char> inputEnd; // (2)

ostream_iterator<string> output(cout); // (3)

while(input!=inputEnd) { // (4)

*output=string("Symbol: " + (*input) +"\n"); // (5)

++input; // (6)

++output; // (7)

}

(1): Создаем итератор входного потока input, итерирующий поток консольного ввода cin. Этот итератор будет перебирать символы.

(2): Конструктор итератора входного потока без аргументов создает итератор, означающий конец входного потока (законечный). Все такие итераторы считаются равными друг другу, независимо от того, принадлежат они какому-либо потоку или нет.

(3): Создаем итератор выходного потока output, итерирующий поток консольного вывода cout. Он будет перебирать строки.

(4): Равенство итератора входного потока законечному итератору означает конец данных во входном потоке.

Обратим внимание, что итератор inputEnd создавался без аргументов, т.е. он не относится к какому-либо конкретному потоку, тогда как итератор input относится к потоку cin. Тем не менее, библиотека STL устроена так, что когда будет достигнут конец входного потока, итератор input станет равным этому итератору (хоть и не будет его копией): оператор == устроен так, что считает равными все законенчые итераторы входных потоков.

(5): *input возвращает очередной элемент из входного потока, присваивание *output записывает элемент в выходной поток.

(6), (7): Передвигаем итераторы на следующий элемент в каждом из потоков..

На первый взгляд польза таких итераторов кажется небольшой, но она становится очевидной в применении итераторов вместе со стандартным алгоритмам4 STL, работающим с итераторами.

Итераторы вставки (insert iterators).

Вывод данных через итератор, приводит к перезаписи элементов контейнера. Если места в контейнере не достаточно, может произойти выход за пределы допустимого диапазона.

Пример кода:

list<int> l1, l2;

//... Заполнить списки

copy(l1.begin(), l1.end(), l2.begin());

сopy() – один из стандартных алгоритмов, копирующий элементы одной последовательности, заданной парой итераторов (первый и второй параметры), поверх элементов второй последовательности, для которой задан итератор начала (третий параметр), количество копируемых элементов определяется первой последовательностью. Так если размер списка l2 был меньше списка l1, то произойдет выход итератора из допустимого диапазона, что приведет к неопределенному поведению.

STL содержит итераторы вставки, которые позволяют не перезаписывать элементы контейнера, а вставлять их в начало (front_insert_iterator), конец (back_insert_iterator) или произвольную позицию (insert_iterator). Определения этих итераторов находятся в заголовочном файле <iterator>. В дополнение STL содержит три вспомогательные функции, которые могут быть использованы для создания этих итераторов: back_inserter() для создания back_insert_iterator, front_inserter() для создания front_insert_iterator и inserter() для создания insert_iterator. Явно создавать итераторы вставки необходимости нет.

// Создать итератор вставки в начало контейнера с

template<class Cont>

back_insert_iterator<Cont> back_inserter(Cont&c);

// Создать итератор вставки в конец контейнера с

template<class Cont>

front_insert_iterator<Cont> front_inserter(Cont&c);

// Создать итератор вставки, который будет осуществлять вставку перед позицией указываемой итератором p в контейнер с

template<class Cont, class Out>

insert_iterator<Cont> front_inserter(Cont&c, Out p);

Эти функции могут быть использованы следующим образом:

list<int> l1, l2;

//... Заполнить списки

// Добавить содержимое l1 в начало l2

copy(l1.begin(), l1.end(), front_inserter(l2));

// Добавить содержимое l1 в конец l2

copy(l1.begin(), l1.end(), back_inserter(l2));

// Добавить содержимое l1 в l2, начиная вставку со второй позиции

copy(l1.begin(), l1.end(), inserter(l2,l2.begin()++));

Рассмотрим реализацию итератора вставки в конец, она достаточно просто и при этом интересна:

template <class Container>

class back_insert_iterator : public output_iterator

{

protected:

Container& container;

public:

back_insert_iterator(Container& x) : container(x) {}

back_insert_iterator <Container>&

operator=(const Container::value_type& value)

{

container.push_back(value);

return *this;

}

back_insert_iterator<Container>& operator*() { return *this; }

back_insert_iterator<Container>& operator++() { return *this; }

back_insert_iterator<Container>& operator++(int) { return *this; }

};

template <class Container>

back_insert_iterator<Container> back_inserter(Container& x) {

return back_insert_iterator<Container>(x);

}

На первый взгляд кажется странным наличие оператора присваивания итератору некоторого значению типа указываемого объекта, и уж совсем странно выглядят операторы * и ++. Однако посмотрим, что происходит на самом деле.

Пусть имеется объект X типа back_insert_iterator. Напишем *X=a; При этом operator*() возвратит сам объект X и для него вызовется operator=(const Container::value_type& value), который и вставит этот элемент в конец контейнера.

Странная семантика операторов ++ объясняется тем, что они вовсе не должны вызываться. Это – итератор вывода и 2 последовательных увеличения итератора без записи недопустимы. Однако продвижение итератора вызывается в операции присваивания, таким образом лимит продвижений исчерпан, больше ниоткуда вызывать продвижение нельзя.

Похожим образом реализованы классы front_insert_iterator (производящая функция front_inserter) и insert_iterator (inserter).

Соседние файлы в папке lab4-iterators