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