what - bad_alloc c++
¿Cómo lidiar con bad_alloc en C++? (6)
¿Cuál es el comportamiento especificado de C ++ Standard de new
en c ++?
La noción habitual es que si el new
operador no puede asignar memoria dinámica del tamaño solicitado, entonces debería arrojar una excepción de tipo std::bad_alloc
.
Sin embargo, algo más sucede incluso antes de bad_alloc
una excepción bad_alloc
:
C ++ 03 Sección 3.7.4.1.3: dice
Una función de asignación que no puede asignar almacenamiento puede invocar a new_handler actualmente instalado (18.4.2.2), si corresponde. [Nota: Una función de asignación suministrada por el programa puede obtener la dirección del controlador_instalado_instalado actual utilizando la función set_new_handler (18.4.2.3).] Si una función de asignación declarada con una especificación de excepción vacía (15.4), throw (), falla asignar almacenamiento, devolverá un puntero nulo. Cualquier otra función de asignación que no pueda asignar almacenamiento solo deberá indicar la falla arrojando una excepción de la clase std :: bad_alloc (18.4.2.1) o una clase derivada de std :: bad_alloc.
Considere la siguiente muestra 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, el operator new
(lo más probable) no podrá asignar espacio para 100 000 000 enteros, y se outOfMemHandler()
la función outOfMemHandler()
, y el programa se cancelará después de emitir un mensaje de error.
Como se ve aquí, el comportamiento predeterminado del new
operador cuando no puede cumplir una solicitud de memoria, es llamar a la función de new-handler
repetidamente hasta que pueda encontrar suficiente memoria o no haya más manejadores nuevos. En el ejemplo anterior, a menos que llamemos a std::abort()
, se outOfMemHandler()
repetidamente . Por lo tanto, el controlador debe asegurarse de que la siguiente asignación tenga éxito, o registrar otro controlador, o registrar ningún controlador, o no devolver (es decir, finalizar el programa). Si no hay un nuevo controlador y la asignación falla, el operador emitirá una excepción.
¿Qué es new_handler
y set_new_handler
?
new_handler
es un typedef para un puntero a una función que toma y no devuelve nada, y set_new_handler
es una función que toma y devuelve un new_handler
.
Algo como:
typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();
El parámetro set_new_handler es un puntero al operador de función new
debe llamar si no puede asignar la memoria solicitada. Su valor de retorno es un puntero a la función del controlador previamente registrado, o nulo si no había un controlador anterior.
¿Cómo manejar las condiciones de falta de memoria en C ++?
Dado el comportamiento de un new
programa de usuario bien diseñado debe manejar las condiciones de new_handler
de memoria proporcionando un new_handler
adecuado que hace una de las siguientes new_handler
:
Haga que haya más memoria disponible: esto puede permitir que el siguiente intento de asignación de memoria dentro del ciclo del operador nuevo sea exitoso. Una forma de implementar esto es asignar un gran bloque de memoria al inicio del programa, luego libérelo para usarlo en el programa la primera vez que se invoque al nuevo manejador.
Instale un nuevo controlador diferente: si el controlador nuevo actual no puede hacer que haya más memoria disponible, y si hay otro controlador nuevo que pueda, entonces el controlador nuevo actual puede instalar el otro controlador nuevo en su lugar ( llamando a set_new_handler
). La próxima vez que operator new llame a la función new-handler, obtendrá la última instalada.
(Una variación de este tema es que un controlador nuevo modifique su propio comportamiento, por lo que la próxima vez que se invoca, hace algo diferente. Una forma de lograr esto es hacer que el controlador nuevo modifique la estática, el espacio de nombre específico o datos globales que afectan el comportamiento del nuevo manejador).
Desinstale el controlador nuevo: esto se hace pasando un puntero nulo a set_new_handler
. Sin un controlador nuevo instalado, el operator new
arrojará una excepción ((convertible a) std::bad_alloc
) cuando la asignación de memoria no sea exitosa.
Lanza una excepción convertible a std::bad_alloc
. Tales excepciones no serán detectadas por el operator new
, sino que se propagarán al sitio que origina la solicitud de memoria.
No devuelto: llamando a abort
o exit
.
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 manera 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 para esto?
¿De qué otra manera puedo lidiar con bad_alloc
en C ++?
Deje que su programa foo exit de forma controlada:
#include <stdlib.h> /* exit, EXIT_FAILURE */
try {
foo();
} catch (const std::bad_alloc&) {
exit(EXIT_FAILURE);
}
Luego escriba un programa shell que llame al programa real. Dado que los espacios de direcciones están separados, el estado de su programa de shell siempre está bien definido.
En general, no puede ni debe intentar responder a este error. bad_alloc
indica que no se puede asignar un recurso porque no hay suficiente memoria disponible. En la mayoría de los escenarios, su programa no puede hacer frente a eso, y terminar pronto es el único comportamiento significativo.
Peor aún, los sistemas operativos modernos suelen malloc
: malloc
y new
siempre devolverán un puntero válido, incluso si técnicamente no queda (o no hay suficiente) memoria libre, por lo que std::bad_alloc
nunca se std::bad_alloc
, o al menos no un signo confiable de agotamiento de la memoria. En su lugar, los intentos de acceder a la memoria asignada generarán un error que no es detectable.
Lo único que podría hacer al capturar std::bad_alloc
es quizás registrar el error e intentar garantizar la finalización segura del programa liberando recursos excepcionales (pero esto se hace automáticamente en el curso normal del desenrollado de la pila después de que se produce el error si el programa usa RAII apropiadamente).
En ciertos casos, el programa puede intentar liberar algo de memoria y volver a intentarlo, o usar memoria secundaria (= disco) en lugar de RAM, pero estas oportunidades solo existen en escenarios muy específicos.
No recomendaría esto, ya que bad_alloc
significa que te has bad_alloc
sin memoria . Lo mejor sería simplemente darse por vencido 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;
}
Puedes verlo como cualquier otra excepción:
try {
foo();
}
catch (const std::bad_alloc&) {
return -1;
}
Todo depende de ti, pero definitivamente es factible desde el punto de vista técnico.
Puedo sugerir una solución más simple (y aún más rápida) para esto. new
operador devolverá 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!