Раз уж речь зашла о буферизованных итераторах, следует упомянуть и об использовании ostreambuf_iterator
при неформатном посимвольном выводе. По сравнению с ostream_iterator
итераторы ostreambuf_iterator
обладают меньшими затратами (при меньших возможностях), поэтому обычно они превосходят их по эффективности.
В начале главы 1 я упоминал о том, что львиная доля репутации STL связана с контейнерами, и это вполне объяснимо. Контейнеры обладают массой достоинств и упрощают повседневную работу бесчисленных программистов C++. Но и алгоритмы STL тоже по-своему замечательны и в той же степени облегчают бремя разработчика. Существует более 100 алгоритмов, и встречается мнение, что они предо-ставляют программисту более гибкий инструментарий по сравнению с контейнерами (которых всего-то восемь!). Возможно, недостаточное применение алгоритмов отчасти и объясняется их количеством. Разобраться в восьми типах контейнеров проще, чем запомнить имена и предназначение многочисленных алгоритмов.
В этой главе я постараюсь решить две основные задачи. Во-первых, я представлю некоторые малоизвестные алгоритмы и покажу, как с их помощью упростить себе жизнь. Не беспокойтесь, вам не придется запоминать длинные списки имен. Алгоритмы, представленные в этой главе, предназначены для решения повседневных задач — сравнение строк без учета регистра символов, эффективный поиск n
объектов, в наибольшей степени соответствующих заданному критерию, обобщение характеристик всех объектов в заданном интервале и имитация copy_if
(алгоритм из исходной реализации HP STL, исключенный в процессе стандартизации).
Во-вторых, я научу вас избегать стандартных ошибок, возникающих при работе с алгоритмами. Например, при вызове алгоритма remove и его родственников remove_if
и unique
необходимо точно знать, что эти алгоритмы делают (и чего они не делают). Данное правило особенно актуально при вызове remove
для интервала, содержащего указатели. Многие алгоритмы работают только с отсортированными интервалами, и программист должен понимать, что это за алгоритмы и почему для них установлено подобное ограничение. Наконец, одна из наиболее распространенных ошибок, допускаемых при работе с алгоритмами, заключается в том, что программист предлагает алгоритму записать результаты своей работы в несуществующую область памяти. Я покажу, как это происходит и как предотвратить эту ошибку.
Возможно, к концу главы вы и не будете относиться к алгоритмам с тем же энтузиазмом, с которым обычно относятся к контейнерам, но по крайней мере будете чаще применять их в своей работе.
Совет 30. Следите за тем, чтобы приемный интервал имел достаточный размер
Контейнеры STL автоматически увеличиваются с добавлением новых объектов (функциями insert
, push_front
, push_back
и т. д.). Автоматическое изменение размеров чрезвычайно удобно, и у многих программистов создается ложное впечатление, что контейнер сам обо всем позаботится и им никогда не придется следить за наличием свободного места. Если бы так!
Проблемы возникают в ситуации, когда программист думает о вставке объектов в контейнер, но не сообщает о своих мыслях STL. Типичный пример:
int transmogrify(int х); // Функция вычисляет некое новое значение
// по переданному параметру х
vector values;
… // Заполнение вектора values данными
vector results; // Применить transmogrify к каждому объекту
transform(values.begin(), // вектора values и присоединить возвращаемые
values.end(), // значения к results.
results.end(), // Фрагмент содержит ошибку!
transmogrify);
В приведенном примере алгоритм transform
получает информацию о том, что приемный интервал начинается с results.end
. С этой позиции он и начинает вывод значений, полученных в результате вызова transmogrify
для каждого элемента values
. Как и все алгоритмы, использующие приемный интервал, transform
записывает свои результаты, присваивая значения элементам заданного интервала. Таким образом, transform
вызовет transmogrify
для values[0]
и присвоит результат *results.end()
. Затем функция transmogrify
вызывается для values[1]
с присваиванием результата *(results.end()+1)
. Происходит катастрофа, поскольку в позиции *results.end()
(и тем более в *(results.end()+1))
не существует объекта! Вызов transform
некорректен из-за попытки присвоить значение несуществующему объекту (в совете 50 объясняется, как отладочная реализация STL позволит обнаружить эту проблему на стадии выполнения).
Читать дальше