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

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

vector v(start, end);

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

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

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

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

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

copy(v.begin(), v.end(), ostream_iterator(cout, ", "));

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

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

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

#include

#include

#include

#include

#include

using namespace std;

template

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

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

}

template

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 start(cin);

 istream_iterator end;

 vector v(start, end);

 printContainer(v);

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

}

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

<p>Глава 8</p><p>Классы</p><p>8.0. Введение</p>
Перейти на страницу:

Похожие книги