Как меняются size и capacity std::vector после вызова clear() и почему это важно при повторном заполнении?
После clear() размер std::vector становится равен нулю, но его capacity не уменьшается. Выделенная память обычно сохраняется, поэтому последующее заполнение может происходить без новых аллокаций, пока число элементов не превысит прежнюю вместимость.
std::vector проектировался как динамический массив с разделением двух понятий: количества фактически созданных элементов и объёма зарезервированного хранилища. Это позволяет удалить элементы, сохранив память для повторного использования, вместо обязательного освобождения и последующего выделения памяти.
Такой подход особенно полезен в циклических или пакетных задачах, где контейнер многократно очищается и заполняется объектами сопоставимого размера.
Ошибочно считать, что clear() возвращает память операционной системе или эквивалентен созданию нового пустого вектора. Это может привести к неправильной оценке производительности и к неожиданно большому удержанию памяти, если вектор временно разросся.
После clear() элементы уничтожаются, поэтому ссылки, указатели и итераторы на них использовать нельзя. Старый итератор конца диапазона также нельзя считать действительным после операции.
size() показывает число живых элементов, а capacity() — количество элементов, для которых уже выделено место. Метод clear() уничтожает все объекты в диапазоне, но не обязан освобождать буфер и не уменьшает capacity().
После очистки вектор остаётся пригодным для повторного использования: новые элементы конструируются в уже выделенной памяти. Однако это не означает, что адреса прежних элементов можно использовать: сами объекты уничтожены, а любые операции, способные вызвать реаллокацию, могут сделать недействительными ранее полученные адреса.
Если память нужно попытаться освободить, применяют shrink_to_fit(), но это лишь необязательная рекомендация реализации. Надёжнее гарантированно освободить буфер через обмен с временным пустым вектором, если сохранение ёмкости не требуется.
Сервис обрабатывает пакеты данных: перед обработкой каждого пакета вектор очищается, затем заполняется заново. Вариант с clear() сохраняет буфер и избегает повторных аллокаций, что уменьшает накладные расходы и фрагментацию памяти.
Альтернатива — создавать новый вектор для каждого пакета. Она проще с точки зрения времени жизни, но может приводить к частым выделениям памяти. Вызов shrink_to_fit() после каждого пакета также нежелателен: он может вызвать перераспределение памяти и свести преимущество повторного использования на нет.
Выбор clear() оправдан, если рабочий объём примерно стабилен. Если вектор однажды обработал огромный пакет и затем долго хранится с большой незадействованной ёмкостью, стоит отдельно рассмотреть освобождение памяти после завершения пикового этапа.
Гарантирует ли clear() освобождение памяти?
Нет. Он уничтожает элементы и устанавливает размер в ноль, но capacity() не уменьшается. Освобождение буфера не является частью гарантии clear().
Можно ли использовать указатель на элемент после clear(), если вектор не перевыделял память?
Нет. Проблема не только в адресе памяти, но и во времени жизни объекта: clear() уничтожает все элементы. Обращение через старый указатель, ссылку или итератор не даёт действительного объекта и приводит к неопределённому поведению.
Чем отличаются clear(), resize(0) и shrink_to_fit()?
clear() уничтожает все элементы и оставляет вместимость без уменьшения. resize(0) также удаляет элементы до нулевого размера и обычно сохраняет вместимость; его главное назначение — изменение размера до указанного значения. shrink_to_fit() просит уменьшить неиспользуемую ёмкость, но реализация может проигнорировать этот запрос; сам по себе он не удаляет элементы.