Что происходит с элементами контейнера при реаллокации (например, в std::vector)?

«Что происходит с элементами контейнера при реаллокации (например, в std::vector)?» — вопрос из категории STL, который задают на 25% собеседований C/C++ Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

При реаллокации (когда std::vector исчерпывает выделенную capacity) происходит следующее:

  1. Выделение новой памяти: Аллокатор выделяет новый, больший блок памяти (обычно в 1.5 или 2 раза больше предыдущего).
  2. Перенос элементов: Все существующие элементы переносятся из старой памяти в новую.
    • Если у типа элемента есть небросающий (noexcept) конструктор перемещения, он будет использован для эффективного переноса ресурсов.
    • В противном случае используется конструктор копирования.
  3. Уничтожение старых объектов: После успешного переноса все элементы в старой памяти уничтожаются (вызываются их деструкторы), и старая память освобождается.

Критические последствия:

  • Инвалидация итераторов и указателей: Все итераторы, указатели и ссылки на элементы вектора становятся невалидными.
  • Гарантия исключений: Если используется конструктор перемещения, объявленный как noexcept (или не бросающий исключений), реаллокация обеспечивает строгую гарантию безопасности исключений. Если перемещение может бросить исключение, используется копирование, что может привести к базовой гарантии (программа остаётся в корректном состоянии, но элементы могут быть утеряны).

Пример:

#include <vector>
#include <string>

int main() {
    std::vector<std::string> vec = {"first", "second"}; // capacity может быть 2
    vec.push_back("third"); // Триггер реаллокации
    // Элементы "first" и "second" перемещаются в новую память,
    // так как std::string имеет noexcept move constructor.
    // Старая память освобождается.
    return 0;
}

Совет: Используйте reserve() для предварительного выделения памяти, если известно примерное количество элементов, чтобы избежать множественных реаллокаций.