Разберите последствия вызова std::vector::reserve: почему после увеличения capacity контейнер не содержит новых элементов?
std::vector::reserve изменяет только вместимость, то есть объём заранее выделенной памяти, но не логический размер контейнера. Поэтому после reserve значение size() остаётся прежним: новые элементы не создаются и не становятся доступными по индексу.
std::vector хранит элементы в непрерывной области памяти и расширяется по мере добавления данных. Если памяти недостаточно, контейнер выделяет новый блок, перемещает или копирует элементы и освобождает старый.
Такой механизм удобен, но повторные перераспределения могут быть дорогими. reserve появился как средство заранее сообщить контейнеру ожидаемый объём данных и тем самым уменьшить количество перераспределений.
Главная ошибка — воспринимать зарезервированную память как уже созданные элементы. Зарезервированная область не входит в диапазон [begin(), end()), поэтому обращение к ней через индекс или итератор не является доступом к элементу контейнера.
Нужно различать два состояния: size() показывает количество существующих объектов, а capacity() — количество элементов, для которых память уже может быть предоставлена без нового перераспределения. Неверное смешение этих понятий приводит к неопределённому поведению или к логическим ошибкам.
Вызов reserve(n) гарантирует, что вместимость будет не меньше n, если текущая вместимость меньше этого значения. Он не уменьшает вместимость и не изменяет размер контейнера.
Например:
После reserve(100) размер равен нулю. Только push_back, emplace_back или операция, изменяющая размер, создаёт элемент и увеличивает size().
Если reserve действительно увеличивает вместимость, прежние итераторы, указатели и ссылки на элементы обычно становятся недействительными из-за перераспределения памяти. Если запрошенная вместимость не больше текущей, перераспределение не происходит, и эти объекты не инвалидируются этим вызовом.
Для создания элементов применяется resize. Вызов resize(n) при увеличении размера добавляет элементы до количества n, тогда как reserve(n) лишь подготавливает память. Например, после resize(100) у вектора уже есть сто элементов, а после reserve(100) их может не быть ни одного.
Предварительное резервирование полезно, когда приблизительное количество элементов известно заранее: оно снижает число дорогих перераспределений и перемещений. Однако чрезмерный reserve может преждевременно занять много памяти, а ручное резервирование перед каждым добавлением может ухудшить стратегию роста контейнера и привести к лишним перераспределениям.
Сервис собирает несколько миллионов записей перед пакетной обработкой. Рассматривались два варианта: добавлять записи без предварительной оценки или вызвать reserve по ожидаемому числу записей.
Первый вариант проще и сохраняет автоматическую стратегию роста vector, но при расширении возможны неоднократные выделения памяти и перемещения элементов. Второй вариант обычно уменьшает число перераспределений, однако при ошибочной оценке может выделить избыточный объём памяти.
Было выбрано резервирование по верхней реалистичной оценке, а не по теоретическому максимуму. В результате элементы продолжили добавляться через emplace_back, размер контейнера отражал только реально полученные записи, а затраты на перераспределения снизились без неоправданного расхода памяти.
Нет. Память может быть выделена, но объекты элементов в этой области не считаются созданными членами контейнера. Доступ через operator[], at() или итераторы за пределами size() не является корректным обращением к элементу; в частности, operator[] не проверяет границы и не создаёт объект.
reserve неизменность адресов элементов при последующих добавлениях?Нет, он гарантирует это только пока размер не превысит текущую вместимость и не потребуется новое перераспределение. После превышения вместимости vector может выделить новый блок, из-за чего прежние ссылки, указатели и итераторы станут недействительными.
Даже если заранее зарезервировано больше памяти, это не является бессрочной гарантией. Кроме того, отдельный вызов reserve сам может инвалидировать прежние обращения к элементам, если он увеличивает вместимость.
resize можно заменить на reserve перед заполнением вектора?Нет. resize создаёт элементы, поэтому после него допустим доступ к индексам от нуля до size() - 1. reserve создаёт только запас памяти, поэтому для заполнения нужно использовать операции добавления, например push_back или emplace_back.
Выбор зависит от задачи: если нужны уже существующие элементы заданного размера, применяется resize; если элементы будут поступать постепенно и требуется лишь снизить число перераспределений, применяется reserve.