элемент входной последовательности, точно так же, как vec.begin (vec — это vector) возвращает итератор, который указывает на первый элемент в векторе. Аргумент шаблона string говорит istream_iterator, что элементы в этой последовательности имеют тип string. Аргумент конструктора cin — это входной поток, из которого производится чтение. Однако это абстракция, так как первого элемента не существует, поскольку из cin еще ничего прочитано не было. Это произойдет несколько позже.

Вторая часть итератора входного потока — это маркер конца, который создается вот так.

istream_iterator<string> end;

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

Последней частью методики использования istream_iterator является его использование для извлечения значений. Удобным способом вытащить в контейнер все значения, введенные в поток, является использование конструктора диапазона контейнера. Например, если создать vector с двумя итераторами, то его конструктор скопирует в контейнер все элементы диапазона, определяемого итераторами. Если передать только что созданные итераторы start и end, то это будет выглядеть так.

vector<string> v(start, end);

Именно здесь происходит чтение значений из потока. При создании v он начинает со start и перебирает все значения, пока не достигнет end. Каждый раз, когда v читает из *start, происходит нечто эквивалентное такому вызову cin.

cin >> v[i]; // v - это vector<string>

Другими словами, следующее значение, извлекаемое из cin, преобразуется в string и вставляется в vector.

При использовании cin как входного потока маркер конца файла, который отмечает конец потока, определяется используемой платформой. В Windows для завершения входного потока требуется нажать на Enter, Ctrl-Z, Enter. Чтобы увидеть, что требуется сделать на вашей платформе, проведите эксперименты, но велика вероятность, что будут использоваться эти же клавиши.

Итераторы выходных потоков ведут себя аналогично итераторам потоков ввода. В примере 7.11 я копирую значения из своего vector в cout, создав для этого ostream_iterator, который указывает на cout, следующим образом.

copy(v.begin(), v.end(), ostream_iterator<string>(cout, ', '));

Аргумент шаблона ostream_iterator говорит, что записываемые элементы будут иметь тип string. Первый аргумент конструктора ostream_iterator — это поток, в который будет производиться запись (и который может быть любым потоком вывода, включая ofstream и ostringstream), а второй это используемый разделитель. Это дает удобный способ выводить диапазон значений на стандартный вывод, что я часто делаю при отладке.

Если требуется дополнительное управление внешним видом вывода, например вывод последовательности в квадратных или фигурных скобках или отсутствие последнего разделителя в конце последовательности, то это потребует всего нескольких дополнительных строк кода. Пример 7.12 показывает тело printContainer и printRange, первая из которых используется в примерах этой главы.

Пример 7.12. Написание собственной функции печати

#include <iostream>

#include <string>

#include <algorithm>

#include <iterator>

#include <vector>

using namespace std;

template<typename C>

void printContainer(const C& c, char delim = ',', ostream& out = cout) {

 printRange(c.begin(), c.end(), delim, out);

}

template<typename Fwd>

void printRange(Fwd first, Fwd last, char delim = ',', ostream& out = cout) {

 out << '{';

 while (first != last) {

  out << *first;

  if (++first != last)

   out << delim << ' ';

 }

 out << '}' << endl;

}

int main() {

 cout << 'Введите набор строк: ';

 istream_iterator<string> start(cin);

 istream_iterator<string> end;

 vector<string> v(start, end);

 printContainer(v);

 printRange(v.begin(), v.end(), ';', cout);

}

Функция printRange представляет собой более общий подход, так как оперирует с диапазонами (более подробно это объясняется в рецепте 7.10), но printContainer более удобна для печати целого контейнера. Имеется множество других способов сделать это. В голову также приходит определение версии operator<<, которая бы работала с выходным потоком и контейнером, и использование стандартного алгоритма for_each с собственным функтором для записи элементов в поток.

Глава 8

Классы

8.0. Введение

Эта глава содержит решения проблем, часто возникающих при работе с классами С++. Рецепты по

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

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

Отметить Добавить цитату