Последней частью методики использования 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с собственным функтором для записи элементов в поток.
Эта глава содержит решения проблем, часто возникающих при работе с классами С++. Рецепты по большей части независимы, но разбиты на две части, каждая из которых занимает примерно по половине главы. Первая половина главы содержит решения проблем, которые могут возникнуть при создании объектов классов, таких как использование функции для создания объекта (которая часто называется шаблоном фабрики) или использование конструкторов и деструкторов для управления ресурсами. Вторая половина содержит решения проблем, возникающих после создания объектов, таких как определение типа объекта во время выполнения, а также некоторые методики реализации наподобие создания интерфейса с помощью абстрактного базового класса.
Конечно, классы — это главная особенность С++, которая обеспечивает возможность объектно-ориентированного программирования, и с ними можно выполнять очень много разных действий. Эта глава не содержит рецептов, объясняющих основы классов: виртуальные функции (полиморфизм), наследование и инкапсуляцию. Я полагаю, что вы уже знакомы с этими основными принципами объектно-ориентированного проектирования независимо от используемого языка программирования. Напротив, целью этой главы является описание принципов некоторых механических сложностей, с которыми можно столкнуться при реализации объектно-ориентированного дизайна на С++.
Читать дальше