Стандартные контейнеры используют специальное значение «один после конца», указывающее на точку, где должно остановиться использование алгоритма. Так как итератор входного потока не имеет в памяти последнего элемента, он для создания логической конечной точки, представляющей точку остановки использования алгоритма, использует конструктор без аргументов.
Последней частью методики использования istream_iterator является его использование для извлечения значений. Удобным способом вытащить в контейнер все значения, введенные в поток, является использование конструктора диапазона контейнера. Например, если создать vector с двумя итераторами, то его конструктор скопирует в контейнер все элементы диапазона, определяемого итераторами. Если передать только что созданные итераторы start и end, то это будет выглядеть так.
vector
Именно здесь происходит чтение значений из потока. При создании 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
Аргумент шаблона ostream_iterator говорит, что записываемые элементы будут иметь тип string. Первый аргумент конструктора ostream_iterator — это поток, в который будет производиться запись (и который может быть любым потоком вывода, включая ofstream и ostringstream), а второй это используемый разделитель. Это дает удобный способ выводить диапазон значений на стандартный вывод, что я часто делаю при отладке.
Если требуется дополнительное управление внешним видом вывода, например вывод последовательности в квадратных или фигурных скобках или отсутствие последнего разделителя в конце последовательности, то это потребует всего нескольких дополнительных строк кода. Пример 7.12 показывает тело printContainer и printRange, первая из которых используется в примерах этой главы.
#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
istream_iterator
vector
printContainer(v);
printRange(v.begin(), v.end(), ';', cout);
}
Функция printRange представляет собой более общий подход, так как оперирует с диапазонами (более подробно это объясняется в рецепте 7.10), но printContainer более удобна для печати целого контейнера. Имеется множество других способов сделать это. В голову также приходит определение версии operator<<, которая бы работала с выходным потоком и контейнером, и использование стандартного алгоритма for_each с собственным функтором для записи элементов в поток.
Глава 8
Классы
8.0. Введение