results.reserve(results.size+values.size); // Обеспечить наличие

                                               // в векторе results

                                               // емкости для value.size

                                               // элементов

transform(values.begin, values.end,               // То же, что и ранее,

 inserter(results, results.begin+results.size/2), // но без лишних

 transmogrify);                                       // перераспределений памяти

При использовании функции reserve для повышения эффективности серии вставок всегда помните, что reserve увеличивает только емкость контейнера, а размер остается неизменным. Даже после вызова reserve при работе с алгоритмом, который должен включать новые элементы в vector или string, необходимо использовать итератор вставки (то есть итератор, возвращаемый при вызове back_inserter, front_inserter или inserter).

Чтобы это стало абсолютно ясно, рассмотрим ошибочный путь повышения эффективности для примера, приведенного в начале совета (с присоединением результатов обработки элементов values к results):

vector values; // См. ранее

vector results;

results.reserve(results.size+values.size); // См. ранее

transform(values.begin, values.end, // Результаты вызова

 results.end,                         // transmogrify записываются

 transmogrify);                         // в неинициализированную

                                        // память; последствия

                                        // непредсказуемы!

В этом фрагменте transform в блаженном неведении пытается выполнить присваивание в неинициализированной памяти за последним элементом results. Обычно подобные попытки приводят к ошибкам времени выполнения, поскольку операция присваивания имеет смысл лишь для двух объектов, но не между объектом и двоичным блоком с неизвестным содержимым. Но даже если этот код каким-то образом справится с задачей, вектор results не будет знать о новых «объектах», якобы созданных в его неиспользуемой памяти. С точки зрения results вектор после вызова transform сохраняет прежний размер, а его конечный итератор будет указывать на ту же позицию, на которую он указывал до вызова transform. Мораль? Использование reserve без итератора вставки приводит к непредсказуемым последствиям внутри алгоритмов и нарушению целостности данных в контейнере.

В правильном решении функция reserve используется в сочетании с итератором вставки:

vector values; // См. ранее

vector results;

results.reserve(results.size+values.size); // См. ранее

transform(values.begin, values.end, // Результаты вызова

 back_inserter(results),                // transmogrify записываются

 transmogrify);                         // в конец вектора results

                                        // без лишних перераспределений

                                        // памяти

До настоящего момента предполагалось, что алгоритмы (такие как transform) записывают результаты своей работы в контейнер в виде новых элементов. Эта ситуация является наиболее распространенной, но иногда новые данные требуется записать поверх существующих. В таких случаях итератор вставки не нужен, но вы должны в соответствии с данным советом проследить за тем, чтобы приемный интервал был достаточно велик.

Допустим, вызов transform должен записывать результаты в results поверх существующих элементов. Если количество элементов в results не меньше их количества в values, задача решается просто. В противном случае придется либо воспользоваться функцией resize для приведения results к нужному размеру:

vector results;

if (results.size < values.size) { // Убедиться в том, что размер

 results.resize(values.size);       // results по крайней мере

}                                     // не меньше размера values

transform(values,begin, values.end, // Перезаписать первые

 back_inserter(results), // values.size элементов results

Перейти на страницу:

Похожие книги