Может ли у std::vector не оказаться памяти для добавления элемента?

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

Ответ

Да, может. При добавлении элемента (например, с помощью push_back, insert, emplace_back или при увеличении размера через resize) может потребоваться перераспределение памяти, если текущая ёмкость (capacity) недостаточна. Если операционная система не может выделить запрошенный блок памяти, std::vector генерирует исключение std::bad_alloc.

Пример, демонстрирующий эту ситуацию:

#include <vector>
#include <iostream>
#include <new> // для std::bad_alloc

int main() {
    std::vector<int> vec;
    vec.reserve(10); // Выделяем память под 10 элементов

    try {
        // Исчерпываем зарезервированную память
        for (int i = 0; i < 10; ++i) {
            vec.push_back(i); // Не вызывает перераспределения
        }

        // Эта операция потребует нового выделения памяти
        std::cout << "Попытка добавить элемент, требующий перераспределения...n";
        vec.push_back(10); // Может выбросить std::bad_alloc

    } catch (const std::bad_alloc& e) {
        std::cerr << "Ошибка выделения памяти: " << e.what() << 'n';
        std::cerr << "Текущий размер (size) вектора: " << vec.size() << 'n';
        std::cerr << "Текущая ёмкость (capacity) вектора: " << vec.capacity() << 'n';
        return 1;
    }

    std::cout << "Элемент успешно добавлен.n";
    return 0;
}

Как с этим работать:

  1. Использование try-catch: Критичный код, работающий с вектором (особенно в средах с ограниченной памятью), должен быть готов обработать std::bad_alloc.
  2. Метод noexcept reserve: Начиная с C++11, метод reserve() не гарантирует успеха и также может выбросить std::bad_alloc.
  3. Проверка доступности памяти (нестандартные методы): В некоторых реализациях STL (например, Microsoft VC++) есть метод _Has_unused_capacity(), но полагаться на него не стоит из-за непереносимости.
  4. Использование std::vector::capacity(): Можно вручную проверять capacity() и сравнивать с size(), чтобы заранее знать, вызовет ли следующее добавление перераспределение. Однако это не защищает от сбоя самого выделения.

Важно: После выброса std::bad_alloc вектор остается в валидном, но, возможно, измененном состоянии. Согласно стандарту, если операция вставки завершается исключением, гарантируется сохранение базовой инвариантности контейнера (например, size() и capacity() остаются прежними, если перераспределение не началось).