Pregunta

Existe 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 una manera que puedo usar un try-catch bloque de detener a este error, a partir de la terminación de mi programa (todo lo que quiero hacer es volver -1)?

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

Cómo puedo lidiar con bad_alloc en C++?

¿Fue útil?

Solución

Puede atraparlo como cualquier otra excepción:

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

Todo lo que puede hacer útilmente de este punto depende de usted, pero definitivamente es factible técnicamente.

Otros consejos

En general, usted no puede , y no debe intentar , para responder a este error. bad_alloc indica que un recurso no se puede asignar porque no suficiente memoria disponible. En la mayoría de los escenarios, su programa no puede esperar hacer frente a eso, y la terminación pronto es el único comportamiento significativo.

Modernos sistemas operativos Modernos a menudo sobresalen: en dichos sistemas, malloc y new puede un puntero válido incluso si no hay suficiente memoria libre izquierda, std::bad_alloc nunca será arrojado, o al menos no es un signo confiable de memoria agotamiento. En su lugar, los intentos de Acceso La memoria asignada dará lugar a una falla de segmentación, que no es cogida (puede manejar la señal de falla de segmentación, pero no puede reanudar el programa después ).

Lo único que podría hacer al captura std::bad_alloc es tal vez registrar el error e intente asegurar una terminación segura del programa liberando recursos pendientes (pero esto se realiza automáticamente en el curso normal de la pila de desenrolamiento después de que el error se lanza si El programa utiliza RAII apropiadamente).

En ciertos casos, el programa puede intentar liberar un poco de memoria e intentar nuevamente, o usar la memoria secundaria (= disco) en lugar de RAM, pero estas oportunidades solo existen en escenarios muy específicos con condiciones estrictas:

  1. La solicitud debe asegurarse de que se ejecuta en un sistema que no esté compitiendo demasiado la memoria , es decir, señala la falla en la asignación en lugar de que tarde.
  2. La solicitud debe ser capaz de liberar la memoria inmediatamente , sin ninguna mayor asignación accidental mientras tanto.

    Es extremadamente raro que las aplicaciones tengan control sobre el punto 1 - Aplicaciones de espacio de usuario nunca , es una configuración en todo el sistema que requiere permisos de raíz para cambiar. 1

    OK, así que vamos a suponer que ha corregido el punto 1. Lo que ahora puede hacer es, por ejemplo, usar un LRU caché para algunos de sus datos (probablemente algunos objetos comerciales particularmente grandes que puedan ser regenerados o recargados a pedido). A continuación, debe colocar la lógica real que pueda fallar en una función que admite reintentar: en otras palabras, si se aborta, puede simplemente relanzarlo:

    lru_cache<widget> widget_cache;
    
    double perform_operation(int widget_id) {
        std::optional<widget> 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 perform_operation(widget_id);
        } catch (std::bad_alloc const&) {
            if (widget_cache.empty()) throw; // memory error elsewhere.
            widget_cache.remove_oldest();
        }
    }
    
    // Handle too many failed attempts here.
    

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


    1 Si está creando una aplicación que hace Control Point 1, y está leyendo esta respuesta, envíeme un correo electrónico, soy genuinamente curioso sobre sus circunstancias.

¿Cuál es el Estándar de C++ se especifica el comportamiento de new en c++?

La noción usual es que si new el operador no puede asignar memoria dinámica del tamaño solicitado, a continuación, se debe lanzar una excepción de tipo std::bad_alloc.
Sin embargo, algo más sucede incluso antes de que bad_alloc excepción:

C++03 Sección 3.7.4.1.3: dice

Una asignación de función que no puede asignar el almacenamiento puede invocar el actualmente instalado new_handler(18.4.2.2), si los hubiere.[Nota:Un programa suministrado por la asignación de la función se puede obtener la dirección de la actualmente instalada new_handler el uso de la set_new_handler función (18.4.2.3).] Si una asignación de función declarada con un vacío excepción de la especificación (15.4), lanzar(), no puede asignar el almacenamiento, deberá devolver un puntero null.Cualquier otra función de asignación que no puede asignar el almacenamiento sólo indicar el error de tiro-ción de una excepción de la clase std::bad_alloc (18.4.2.1) o de una clase derivada de std::bad_alloc.

Considere el siguiente ejemplo de código:

#include <iostream>
#include <cstdlib>

// function to call if operator new can't allocate enough memory or error arises
void outOfMemHandler()
{
    std::cerr << "Unable to satisfy request for memory\n";

    std::abort();
}

int main()
{
    //set the new_handler
    std::set_new_handler(outOfMemHandler);

    //Request huge memory size, that will cause ::operator new to fail
    int *pBigDataArray = new int[100000000L];

    return 0;
}

En el ejemplo anterior, operator new (lo más probable), no podrán asignar espacio para 100.000.000 de números enteros, y la función outOfMemHandler() será llama, y el programa se detendrá después de la emisión de un mensaje de error.

Como se ve aquí el comportamiento predeterminado de new operador cuando no pueda cumplir con una solicitud de memoria, es llamar a la new-handler function repetidamente hasta que se puede encontrar suficiente memoria o no hay más nuevos controladores.En el ejemplo anterior, a menos que llamamos std::abort(), outOfMemHandler() sería llama repetidamente.Por lo tanto, el controlador debe asegurarse de que la próxima asignación se realiza correctamente, o se registra otro controlador, o se registra ningún controlador, o de no retorno (es decir,terminar el programa).Si no hay un nuevo controlador y la asignación de falla, el operador tendrá que lanzar una excepción.

¿Cuál es la new_handler y set_new_handler?

new_handler es un typedef para un puntero a una función que toma y devuelve nada, y set_new_handler es una función que toma y devuelve un new_handler.

Algo así como:

typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();

set_new_handler del parámetro es un puntero a la función de operador new debe llamar en caso de que no se puede asignar la memoria solicitada.Su valor de retorno es un puntero a los registrados previamente en función de controlador, o null si no hay ningún controlador previo.

Cómo manejar las condiciones de falta de memoria en C++?

Dado el comportamiento de newun bien diseñado el programa de usuario debe manejar fuera de la memoria de las condiciones de dotarse de un new_handlerque hace uno de los siguientes:

Disponer de más memoria: Esto puede permitir que la próxima asignación de memoria en el intento dentro de operador new loop para tener éxito.Una forma de implementar esta es asignar un bloque de memoria al inicio del programa, a continuación, suelte para su uso en el programa la primera vez que el nuevo controlador es invocado.

Instalar diferentes nuevas-controlador: Si el nuevo controlador no puede hacer más de memoria disponible, y hay otro nuevo controlador que puede, a continuación, el actual nuevo controlador puede instalar el nuevo controlador en su lugar (llamando set_new_handler).La próxima vez que el operador de nuevo llama a la nueva función de control, será la que más recientemente instalado.

(Una variación de este tema es para un nuevo controlador para modificar su propio comportamiento, de modo que la próxima vez que se invoca, se hace algo diferente.Una forma de lograr esto es tener el nuevo controlador de modificar estática, espacio de nombres específico, o de datos global que afecta el nuevo controlador del comportamiento).

Desinstale el nuevo controlador: Esto se hace pasando un puntero nulo para set_new_handler.Sin nuevos-controlador instalado, operator new se produce una excepción ((convertible) std::bad_alloc) cuando la asignación de memoria es incorrecta.

Lanzar una excepción convertible a std::bad_alloc.Tales excepciones no ser atrapado por el operator new, pero se propagan hacia el sitio de origen de la solicitud para la memoria.

No retorno: Llamando abort o exit.

No sugeriría esto, ya que bad_alloc significa que usted es fuera de la memoria .Sería mejor simplemente renunciar en lugar de intentar recuperarse.Sin embargo, aquí está la solución que está solicitando:

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

Puedo sugerir una solución más simple (e incluso más rápida) para esto.El operador de new devolvería nulo si no se pudo asignar la memoria.

int fv() {
    T* p = new (std::nothrow) T[1000000];
    if (!p) return -1;
    do_something(p);
    delete p;
    return 0;
}

¡Espero que esto pueda ayudar!

Deja que su foo programa salga en un controladoManera:

#include <stdlib.h>     /* exit, EXIT_FAILURE */

try {
    foo();
} catch (const std::bad_alloc&) {
    exit(EXIT_FAILURE);
}

Luego escriba un programa shell que llama al programa real.Dado que los espacios de direcciones están separados, el estado de su programa de shell siempre está bien definido.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top