Кратко: `v.reserve(101010)` выделяет буфер с capacity ≥ 10\,1010\, но size остаётся 0\,00. При `push_back` память перевыделяется каждый раз, когда size достигает capacity. Что произойдёт в вашем фрагменте: - после `v.reserve(101010)` у вас capacity = ≥10\ge 10≥10, size = 0\,00; - первые 10\,1010 вызовов `push_back` не вызывают перевыделения; - 11\,1111-й вызов требует перевыделения в новый буфер с capacity ≥ 11\,1111 (на практике реализация обычно увеличивает capacity экспоненциально — например до 15\,1515 или 20\,2020, но это implementation-defined); - 12\,1212-й вызов уже поместится в новый буфер без дополнительной аллокации. Копирования/перемещения: - при перевыделении все существующие элементы копируются или перемещаются в новый буфер; их число равно предыдущему size (здесь примерно 10\,1010 элементов); - для тривиальных типов (как `int`) это обычно эквивалентно `memmove` и дешево; для пользовательских типов используются move- или copy-конструкторы; - если move-конструктор не `noexcept`, реализация может использовать копирование для сохранения гарантий исключений. Сложности: - отдельный `push_back` — амортизированно O(1)O(1)O(1), но один перевод/перемещение при перевыделении стоит O(k)O(k)O(k), где kkk — текущий size. Как оптимизировать добавление большого числа элементов: - заранее вызвать `v.reserve(N)` с ожидаемым количеством элементов NNN (например `v.reserve(121212)`) — тогда перевыделений не будет; - альтернативно: `v.resize(N)` и затем заполнять по индексу — избегает проверок роста, но при этом элементы value-initialized; - использовать `emplace_back` для конструирования элементов прямо в буфере (экономит копии/перемещения); - при больших вставках использовать `v.insert(v.end(), first, last)` или конструктор из диапазона; - для типов с дорогой семантикой перемещения — сделать move-конструктор `noexcept`, чтобы при перевыделении использовалось перемещение, а не копирование. Пример простого исправления вашего фрагмента: v.reserve(121212); for (int i = 0; i < 121212; ++i) v.push_back(i); Это гарантирует отсутствие перевыделений и перемещений при вставке этих 12\,1212 элементов.
Что произойдёт в вашем фрагменте:
- после `v.reserve(101010)` у вас capacity = ≥10\ge 10≥10, size = 0\,00;
- первые 10\,1010 вызовов `push_back` не вызывают перевыделения;
- 11\,1111-й вызов требует перевыделения в новый буфер с capacity ≥ 11\,1111 (на практике реализация обычно увеличивает capacity экспоненциально — например до 15\,1515 или 20\,2020, но это implementation-defined);
- 12\,1212-й вызов уже поместится в новый буфер без дополнительной аллокации.
Копирования/перемещения:
- при перевыделении все существующие элементы копируются или перемещаются в новый буфер; их число равно предыдущему size (здесь примерно 10\,1010 элементов);
- для тривиальных типов (как `int`) это обычно эквивалентно `memmove` и дешево; для пользовательских типов используются move- или copy-конструкторы;
- если move-конструктор не `noexcept`, реализация может использовать копирование для сохранения гарантий исключений.
Сложности:
- отдельный `push_back` — амортизированно O(1)O(1)O(1), но один перевод/перемещение при перевыделении стоит O(k)O(k)O(k), где kkk — текущий size.
Как оптимизировать добавление большого числа элементов:
- заранее вызвать `v.reserve(N)` с ожидаемым количеством элементов NNN (например `v.reserve(121212)`) — тогда перевыделений не будет;
- альтернативно: `v.resize(N)` и затем заполнять по индексу — избегает проверок роста, но при этом элементы value-initialized;
- использовать `emplace_back` для конструирования элементов прямо в буфере (экономит копии/перемещения);
- при больших вставках использовать `v.insert(v.end(), first, last)` или конструктор из диапазона;
- для типов с дорогой семантикой перемещения — сделать move-конструктор `noexcept`, чтобы при перевыделении использовалось перемещение, а не копирование.
Пример простого исправления вашего фрагмента:
v.reserve(121212);
for (int i = 0; i < 121212; ++i) v.push_back(i);
Это гарантирует отсутствие перевыделений и перемещений при вставке этих 12\,1212 элементов.