¿Cómo tratar bad_alloc en C++?

Hay un método llamado foo que a veces devuelve el siguiente error:

terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Abort

¿Hay alguna forma de que pueda usar un bloque try-catch para evitar que este error termine mi programa (todo lo que quiero hacer es devolver -1)?

Si es así, ¿cuál es la sintaxis?

¿De qué otra forma puedo tratar con bad_alloc en C++?

En general usted no puede, y no debería intentar, responder a este error. bad_alloc indica que un recurso no puede ser asignado porque no hay suficiente memoria disponible. En la mayoría de los escenarios tu programa no puede esperar hacer frente a esto, y terminar pronto es el único comportamiento significativo.

Peor aún, los sistemas operativos modernos a menudo sobre-asignan: en tales sistemas, malloc y new pueden un puntero válido incluso si no queda suficiente memoria libre - std::bad_alloc nunca será lanzado, o al menos no es una señal fiable de agotamiento de memoria. En cambio, los intentos de acceder a la memoria asignada resultarán en un fallo de segmentación, que no es capturable (puedes manipular la señal de fallo de segmentación, pero no puedes reanudar el programa después).

Lo único que se puede hacer cuando se captura std::bad_alloc es quizás registrar el error, e intentar asegurar una terminación segura del programa liberando los recursos pendientes (pero esto se hace automáticamente en el curso normal del desenrollado de la pila después de que se lance el error si el programa usa RAII apropiadamente).

En ciertos casos el programa puede intentar liberar algo de memoria e intentarlo de nuevo, o usar memoria secundaria (= disco) en lugar de RAM pero estas oportunidades sólo existen en escenarios muy específicos con condiciones estrictas:

    1. La aplicación debe asegurarse de que se ejecuta en un sistema que no sobrecompromete memoria, es decir, señala el fallo en el momento de la asignación y no más tarde.
    1. La aplicación debe ser capaz de liberar memoria inmediatamente, sin más asignaciones accidentales mientras tanto.

Es extremadamente raro que las aplicaciones tengan control sobre el punto 1 - las aplicaciones de espacio de usuario nunca lo tienen, es una configuración de todo el sistema que requiere permisos de root para cambiarla;

OK, asumamos que has arreglado el punto 1. Lo que puedes hacer ahora es, por ejemplo, utilizar una caché LRU para algunos de tus datos (probablemente algunos objetos de negocio particularmente grandes que pueden ser regenerados o recargados bajo demanda). A continuación, tienes que poner la lógica real que puede fallar en una función que soporte reintentos - en otras palabras, si se aborta, puedes simplemente relanzarla:

lru_cache widget_cache;

double perform_operation(int widget_id) {
    std::optional maybe_widget = widget_cache.find_by_id(widget_id);
    if (not maybe_widget) {
        maybe_widget = widget_cache.store(widget_id, load_widget_from_disk(widget_id));
    }
    return maybe_widget->frobnicate();
}

...

for (int num_attempts = 0; num_attempts < MAX_NUM_ATTEMPTS; ++num_attempts) {
    try {
        return realizar_operación(widget_id);
    } catch (std::bad_alloc const&) {
        if (widget_cache.empty()) throw; // error de memoria en otro lugar.
        widget_cache.remove_oldest();
    }
}

// Maneja demasiados intentos fallidos aquí.

Pero incluso aquí, usar std::set_new_handler en lugar de manejar std::bad_alloc proporciona el mismo beneficio y sería mucho más simple.


Si estás creando una aplicación que hace controlar el punto 1, y estás leyendo esta respuesta, por favor envíame un email, tengo verdadera curiosidad por tus circunstancias.

Comentarios (26)
Solución

Puede capturarla como cualquier otra excepción:

try {
  foo();
}
catch (const std::bad_alloc&) {
  return -1;
}

Lo que puedas hacer útilmente desde este punto depende de ti, pero es definitivamente factible técnicamente.

Comentarios (0)

Yo no sugeriría esto, ya que bad_alloc significa que estás sin memoria. Lo mejor sería darse por vencido en lugar de intentar recuperarla. Sin embargo aquí está la solución que usted está pidiendo:

try {
    foo();
} catch ( const std::bad_alloc& e ) {
    return -1;
}
Comentarios (5)