Д. Стефенс - C++. Сборник рецептов
- Название:C++. Сборник рецептов
- Автор:
- Жанр:
- Издательство:КУДИЦ-ПРЕСС
- Год:2007
- Город:Москва
- ISBN:5-91136-030-6
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Д. Стефенс - C++. Сборник рецептов краткое содержание
Данная книга написана экспертами по C++ и содержит готовые рецепты решения каждодневных задач для программистов на С++. Один из авторов является создателем библиотеки Boost Iostreams и нескольких других библиотек C++ с открытым исходным кодом. В книге затрагивается множество тем, вот лишь некоторые из них: работа с датой и временем; потоковый ввод/вывод; обработка исключений; работа с классами и объектами; сборка приложений; синтаксический анализ XML-документов; программирование математических задач. Читатель сможет использовать готовые решения, а сэкономленное время и усилия направить на решение конкретных задач.
C++. Сборник рецептов - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
vector v(start, end);
list lst;
copyIf(v.begin(), v.end(), back_inserter >(lst),
bind2nd(less(), "cookie"));
Если попробовать сделать то же самое, использовав в алгоритме один и тот же тип итераторов, то он просто не скомпилируется.
В примере 7.10 я в качестве начала выходного диапазона передаю back_inserter, а не, скажем, итератор, возвращаемый lst.begin. Это делается потому, что lst не содержит элементов, и в этом алгоритме (как и в стандартном алгоритме копирования) целевой диапазон должен быть достаточно большим, чтобы вместить все элементы, которые будут в него скопированы. В противном случае увеличение итератора вывода в copyIfприведет к неопределенному поведению. back_inserterвозвращает итератор вывода, который при его увеличении вызывает для контейнера метод push_back. В результате этого при каждом увеличении выходного итератора размер lstувеличивается на один. Более подробно шаблон класса back_inserterя описываю в рецепте 7.5.
При написании собственного алгоритма для работы с диапазонами (т.е. со стандартными контейнерами) вы должны работать с аргументами-итераторами, а не с аргументами-контейнерами. У вас может возникнуть желание объявить copyIfтак, чтобы он принимал два контейнера, а не итератор исходного и результирующего диапазонов, но это менее обобщенное решение, чем диапазоны. Во-первых, если передавать аргументы-контейнеры, то станет невозможно работать с подмножеством элементов контейнера. Далее, в теле copyIfпоявится зависимость от методов контейнеров beginи end, которые дадут требуемый диапазон, и возвращаемый тип будет зависеть от типа контейнера, используемого в качестве выходного. Это означает, что использование в copyIfнестандартных диапазонов, таких как встроенные массивы или собственные контейнеры, работать не будет. Именно по этим и некоторым другим причинам все стандартные алгоритмы оперируют с диапазонами.
Наконец, если вы пишете свой алгоритм, дважды убедитесь, что стандартные алгоритмы вас не устраивают. На первый взгляд они могут казаться очень простыми алгоритмами, но их кажущаяся простота проистекает из их обобщенности, и в девяти случаях из десяти их можно расширить так, что они подойдут для новых задач. Иногда следует стремиться к повторному использованию стандартных алгоритмов, так как это дает гарантию переносимости и эффективности.
Рецепт 7.5.
7.11. Печать диапазона в поток
Имеется диапазон элементов, который требуется напечатать в поток, например, в coutс целью отладки.
Напишите шаблон функции, который принимает диапазон или контейнер, перебирает все его элементы и использует алгоритм соруи ostream_iteratorдля записи. Если требуется дополнительное форматирование, напишите свой простой алгоритм, который перебирает диапазон и печатает каждый элемент в поток. (См. пример 7.11)
Пример 7.11. Печать диапазона в поток
#include
#include
#include
#include
#include
using namespace std;
int main() {
// Итератор ввода - это противоположность итератору вывода: он
// читает элементы из потока так. как будто это контейнер.
cout << "Введите несколько строк: ";
istream_iterator start(cin);
istream_iterator end;
vector v(start, end);
// Используем выходной поток как контейнер, используя
// output_iterator. Он создает итератор вывода, для которого запись
// в каждый элемент эквивалентна записи в поток.
copy(v.begin(), v.end(), ostreamIterator(cout, ", "));
}
Вывод примера 7.11 может выглядеть так.
Введите несколько строк: z x y a b с
^Z
z, x, y, a, b, с,
Потоковый итератор — это итератор, который основан на потоке, а не на диапазоне элементов контейнера, и позволяет рассматривать поток как итератор ввода (читать из разыменованного значения и увеличивать итератор) или итератор вывода (аналогично итератору ввода, но для записи в разыменованное значение вместо чтения из него). Это облегчает чтение значений (особенно строк) из потока, что делается в нескольких других примерах этой главы, и запись значений в поток, что делается в примере 7.11. Я знаю, что этот рецепт посвящен записи диапазона в поток, но позвольте мне немного отойти от этой задачи и, поскольку я использую потоковые итераторы во многих примерах этой главы, объяснить, что это такое.
В примере 7.11 показаны три ключевые части istream_iterator. Первая часть — это создание istream_iterator, указывающего на начало потокового ввода. Это делается вот так.
istream_iterator start(cin);
В результате создается итератор с именем start, который указывает на первый элемент входной последовательности, точно так же, как vec.begin( vec— это vector) возвращает итератор, который указывает на первый элемент в векторе. Аргумент шаблона stringговорит istream_iterator, что элементы в этой последовательности имеют тип string. Аргумент конструктора cin— это входной поток, из которого производится чтение. Однако это абстракция, так как первого элемента не существует, поскольку из cinеще ничего прочитано не было. Это произойдет несколько позже.
Вторая часть итератора входного потока — это маркер конца, который создается вот так.
istream_iterator end;
Стандартные контейнеры используют специальное значение «один после конца», указывающее на точку, где должно остановиться использование алгоритма. Так как итератор входного потока не имеет в памяти последнего элемента, он для создания логической конечной точки, представляющей точку остановки использования алгоритма, использует конструктор без аргументов.
Последней частью методики использования istream_iteratorявляется его использование для извлечения значений. Удобным способом вытащить в контейнер все значения, введенные в поток, является использование конструктора диапазона контейнера. Например, если создать vectorс двумя итераторами, то его конструктор скопирует в контейнер все элементы диапазона, определяемого итераторами. Если передать только что созданные итераторы startи end, то это будет выглядеть так.
vector v(start, end);
Именно здесь происходит чтение значений из потока. При создании vон начинает со startи перебирает все значения, пока не достигнет end. Каждый раз, когда vчитает из *start, происходит нечто эквивалентное такому вызову cin.
cin >> v[i]; // v - это vector
Другими словами, следующее значение, извлекаемое из cin, преобразуется в stringи вставляется в vector.
Интервал:
Закладка: