Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ для начинающих (Стенли Липпман) 3-е хххх.pdf
Скачиваний:
86
Добавлен:
30.05.2015
Размер:
5.92 Mб
Скачать

С++ для начинающих

569

#include <iterator>

В следующей программе мы пользуемся потоковым итератором чтения для получения из стандартного ввода последовательности целых чисел в вектор, а затем применяем

потоковый итератор записи в качестве целевого в обобщенном алгоритме

#include <iostream> #include <iterator> #include <algorithm> #include <vector> #include <functional>

/*

*вход:

*23 109 45 89 6 34 12 90 34 23 56 23 8 89 23

*выход:

*109 90 89 56 45 34 23 12 8 6

*/

int main()

{

istream_iterator< int > input( cin ); istream_iterator< int > end_of_stream;

vector<int> vec;

copy ( input, end_of_stream, inserter( vec, vec.begin() ));

sort( vec.begin(), vec.end(), greater<int>() );

ostream_iterator< int > output( cout, " " ); unique_copy( vec.begin(), vec.end(), output );

unique_copy():

}

12.4.4. Итератор istream_iterator

Вобщем виде объявление потокового итератора чтения istream_iterator имеет форму: istream_iterator<Type> identifier( istream& );1.

1. Если имеющийся у Вас компилятор пока не поддерживает параметр шаблонов по умолчанию, то конструктору istream_iterator необходимо будет явно передать также и второй аргумент: тип difference_type, способный хранить результат вычитания двух итераторов контейнера, куда помещаются элементы. Например, в разделе 12.2 при изучении программы, которая должна транслироваться компилятором, не поддерживающим параметры шаблонов по умолчанию, мы писали:

typedef vector<string,allocator>::difference_type diff_type istream_iterator< string, diff_type > input_set1( infile1 ), eos; istream_iterator< string, diff_type > input_set2( infile2 );

Примечание [O.A.3]: Нумера ция сносок сбита.

С++ для начинающих

570

где Type это любой встроенный или пользовательский тип класса, для которого определен оператор ввода. Аргументом конструктора может быть объект либо класса istream, например cin, либо производного от него класса с открытым типом

#include <iterator> #include <fstream> #include <string> #include <complex>

//прочитать последовательность объектов типа complex

//из стандартного ввода

istream_iterator< complex > is_complex( cin );

// прочитать последовательность строк из именованного файла ifstream infile( "C++Primer" );

наследования ifstream:

istream_iterator< string > is_string( infile );

При каждом применении оператора инкремента к объекту типа istream_iterator читается следующий элемент из входного потока, для чего используется оператор operator>>(). Чтобы сделать то же самое в обобщенных алгоритмах, необходимо предоставить пару итераторов, обозначающих начальную и конечную позицию в файле. Начальную позицию дает istream_iterator, инициализированный объектом istream, – такой, скажем, как is_string. Для получения конечной позиции мы

//конструирует итератор end_of_stream, который будет служить маркером

//конца потока в итераторной паре

istream_iterator< string > end_of_stream

vector<string> text;

// правильно: передаем пару итераторов copy( is_string, end_of_stream,

inserter( text, text.begin() ));

используем специальный конструктор по умолчанию класса istream_iterator:

12.4.5. Итератор ostream_iterator

Объявление потокового итератора записи ostream_iterator может быть представлено в двух формах:

Если бы компилятор полностью удовлетворял стандарту C++, достаточно было бы написать так:

istream_iterator< string > input_set1( infile1 ), eos; istream_iterator< string > input_set2( infile2 );

С++ для начинающих

571

ostream_iterator<Type> identifier( ostream& )

ostream_iterator<Type> identifier( ostream&, char * delimiter )

где Type это любой встроенный или пользовательский тип класса, для которого определен оператор вывода (operator<<). Во второй форме delimiter это разделитель, то есть C-строка символов, которая выводится в файл после каждого элемента. Такая строка должна заканчиваться двоичным нулем, иначе поведение программы не определено (скорее всего, она аварийно завершит выполнение). В качестве аргумента ostream может выступать объект класса ostream, например cout, либо

#include <iterator> #include <fstream> #include <string> #include <complex>

//записать последовательность объектов типа complex

//в стандартный вывод, разделяя элементы пробелами ostream_iterator< complex > os_complex( cin, " " );

//записать последовательность строк в именованный файл ofstream outfile( "dictionary" );

производного от него класса с открытым типом наследования, скажем ofstream: ostream_iterator< string > os_string( outfile, "\n" );

Вот простой пример чтения из стандартного ввода и копирования на стандартный вывод

#include <iterator> #include <algorithm> #include <iostream>

int main()

{

copy( istream_iterator< int >( cin ), istream_iterator< int >(), ostream_iterator< int >( cout, " " ));

с помощью безымянных потоковых итераторов и обобщенного алгоритма copy():

}

Ниже приведена небольшая программа, которая открывает указанный пользователем файл и копирует его на стандартный вывод, применяя для этого алгоритм copy() и потоковый итератор записи ostream_iterator:

С++ для начинающих

572

#include <string> #include <algorithm> #include <fstream> #include <iterator>

main()

{

string file_name;

cout << "please enter a file to open: "; cin >> file_name;

if ( file_name.empty() || !cin ) {

cerr << "unable to read file name\n"; return -1;

}

ifstream infile( file_name.c_str()); if ( !infile ) {

cerr << "unable to open " << file_name << endl; return -2;

}

istream_iterator< string > ins( infile ), eos; ostream_iterator< string > outs( cout, " " ); copy( ins, eos, outs );

}

12.4.6. Пять категорий итераторов

Для поддержки полного набора обобщенных алгоритмов стандартная библиотека определяет пять категорий итераторов, положив в основу классификации множество операций. Это итераторы чтения (InputIterator), записи (OutputIterator), однонаправленные (ForwardIterator) и двунаправленные итераторы (BidirectionalIterator), а также итераторы с произвольным доступом (RandomAccessIterators). Ниже приводится краткое обсуждение характеристик каждой категории:

итератор чтения можно использовать для получения элементов из контейнера, но поддержка записи в контейнер не гарантируется. Такой итератор должен обеспечивать следующие операции (итераторы, поддерживающие также дополнительные операции, можно употреблять в качестве итераторов чтения при условии, что они удовлетворяют минимальным требованиям): сравнение двух итераторов на равенство и неравенство, префиксная и постфиксная форма инкремента итератора для адресации следующего элемента (оператор ++), чтение элемента с помощью оператора разыменования (*). Такого уровня поддержки требуют, в частности, алгоритмы find(), accumulate() и equal(). Любому алгоритму, которому необходим итератор чтения, можно передавать также и итераторы категорий, описанных в пунктах 3, 4 и 5;

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

Такие итераторы обычно применяются в качестве третьего аргумента алгоритма (например, copy()) и указывают на позицию, с которой надо начинать копировать.