80. Предпочитайте push_back другим способам расширения последовательности
80. Предпочитайте push_back другим способам расширения последовательности
Резюме
Используйте push_back везде, где это возможно. Если для вас не важна позиция вставки нового объекта, лучше всего использовать для добавления элемента в последовательность функцию push_back. Все прочие средства могут оказаться как гораздо менее быстрыми, так и менее понятными.
Обсуждение
Вы можете вставить элементы в последовательность в разных точках с использованием insert; добавить элементы в последовательность можно разными способами, включая следующие:
vector<int> vec; // vec пуст
vec.resize(vec.size() + 1, 1); // vec содержит { 1 }
vec.insert(vec.end(), 2); // vec содержит { 1, 2 }
vec.push_back(3); // vec содержит { 1, 2, 3 }
Среди прочих методов push_back единственный имеет постоянное амортизированное время работы. Время работы других методов хуже — вплоть до квадратичного. Излишне говорить, что при больших размерах данных плохое время работы препятствует масштабируемости (см. рекомендацию 7).
Магия push_back проста: эта функция увеличивает емкость экспоненциально, а не на фиксированное значение. Следовательно, количество перераспределений памяти и копирований быстро уменьшается с увеличением размера. В случае контейнера, который заполняется с использованием только лишь функции push_back, каждый элемент копируется в среднем только один раз — независимо от конечного размера контейнера.
Конечно, resize и insert могут воспользоваться той же стратегией, но это уже зависит от реализации; гарантию дает только push_back.
Алгоритмы не могут непосредственно обращаться к push_back, поскольку они не имеют доступа к контейнерам. Вы можете потребовать от алгоритма использовать push_back, воспользовавшись back_inserter.
Исключения
Если вы добавляете не один элемент, а диапазон, то даже если добавление выполняется в конец контейнера, лучше использовать функцию для вставки диапазона значений (см. рекомендацию 81).
Экспоненциальный рост приводит к расточительному выделению памяти. Для тонкой настройки роста можно явно вызвать функцию reserve — функции push_back, resize и подобные не будут перераспределять память, если ее достаточно для работы. Для получения вектора "правильного размера" следует воспользоваться идиомой "горячей посадки" (см. рекомендацию 82).
Ссылки
[Stroustrup00] §3.7-8, §16.3.5, §17.1.4.1