← Назад к вопросам

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

2.0 Middle🔥 191 комментариев
#STL контейнеры и алгоритмы#Исключения и обработка ошибок#Язык C++

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

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

Да, совершенно может. Это один из критических моментов при работе с std::vector в высоконагруженных приложениях и системном программировании.

Когда vector может не получить память

1. Исчерпание доступной памяти системы

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

std::vector<int> vec;
try {
    for (int i = 0; i < 1000000000; ++i) {
        vec.push_back(i);  // может выбросить std::bad_alloc
    }
} catch (const std::bad_alloc& e) {
    std::cerr << "Не удалось выделить память: " << e.what() << "\n";
}

2. Исчерпание адресного пространства процесса

На 32-битных системах адресное пространство процесса ограничено (~4 GB). Vector может физически не влезть в доступную память:

// На 32-bit системе попытка выделить несколько GB приведёт к std::bad_alloc
std::vector<char> huge_vector(3LL * 1024 * 1024 * 1024); // 3 GB

3. Лимиты ОС и процесса

Многие ОС имеют лимиты на объём памяти, которую может использовать один процесс:

# Linux: проверить лимиты
ulimit -a
# ulimit -v — лимит виртуальной памяти
# ulimit -m — лимит физической памяти

Обработка ошибок выделения памяти

Стандартный подход с исключениями:

#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec;
    
    try {
        vec.reserve(1000000000);  // Может выбросить std::bad_alloc
    } catch (const std::bad_alloc& e) {
        std::cerr << "Ошибка выделения памяти: " << e.what() << "\n";
        return 1;
    }
    
    // Проверка перед добавлением
    if (vec.size() == vec.max_size()) {
        std::cerr << "Vector достиг максимального размера\n";
    }
    
    return 0;
}

Проверка emplace_back с гарантией:

// С C++17 можно использовать try-catch более элегантно
struct Result {
    bool success;
    std::string error_message;
};

Result safe_add_to_vector(std::vector<int>& vec, int value) {
    try {
        vec.push_back(value);
        return {true, ""};
    } catch (const std::bad_alloc& e) {
        return {false, std::string(e.what())};
    }
}

Превентивные меры

1. Предварительное выделение памяти (reserve)

std::vector<int> vec;
vec.reserve(10000);  // Выделяем память один раз
// Теперь несколько push_back не вызовут реаллокацию
for (int i = 0; i < 5000; ++i) {
    vec.push_back(i);  // Не выбросит исключение, если reserve не выбросила
}

2. Проверка доступной памяти

#include <sys/resource.h>

bool has_enough_memory(size_t required_bytes) {
    struct rlimit limit;
    getrlimit(RLIMIT_AS, &limit);  // Лимит виртуальной памяти
    return required_bytes < limit.rlim_cur;
}

3. Кастомный allocator с контролем памяти

template<typename T>
class LimitedAllocator {
    size_t max_bytes;
    size_t used_bytes = 0;
    
public:
    LimitedAllocator(size_t max_bytes) : max_bytes(max_bytes) {}
    
    T* allocate(size_t n) {
        size_t bytes_needed = n * sizeof(T);
        if (used_bytes + bytes_needed > max_bytes) {
            throw std::bad_alloc();
        }
        T* ptr = new T[n];
        used_bytes += bytes_needed;
        return ptr;
    }
    
    void deallocate(T* ptr, size_t n) {
        used_bytes -= n * sizeof(T);
        delete[] ptr;
    }
};

// Использование:
std::vector<int, LimitedAllocator<int>> limited_vec(
    LimitedAllocator<int>(1024 * 1024)  // Максимум 1 MB
);

Важные моменты

  • std::bad_alloc — исключение, которое выбрасывается при неудачном выделении памяти
  • max_size() — возвращает максимально возможный размер вектора (не гарантирует успешное выделение)
  • capacity() — текущий объём выделенной памяти
  • size() — текущее количество элементов
  • На 64-битных системах проблема выделения памяти менее критична, но всё равно возможна

Лучшая практика — всегда обрабатывать исключение std::bad_alloc в критичных для надёжности приложениях и использовать reserve() для предсказуемого поведения.