Errores con el operador new

Revisión del 13:23 12 dic 2017 de Majibacoa2 jc (discusión | contribuciones) (Página creada con «<div align="justify">{{Definición|Nombre= Errores con el operador new |imagen= |concepto=Cuando la asignación de memoria no puede ser satisfecha, new comprueba si existe...»)
(dif) ← Revisión anterior | Revisión actual (dif) | Revisión siguiente → (dif)
Errores con el operador new
Información sobre la plantilla
Concepto:Cuando la asignación de memoria no puede ser satisfecha, new comprueba si existe una función que en caso de error adopte las medidas pertinentes, y en caso de existir, es invocada sin que se le pase ningún argumento
Errores con el operador new . Cuando la asignación de memoria no puede ser satisfecha, new comprueba si existe una función que en caso de error adopte las medidas pertinentes, y en caso de existir, es invocada sin que se le pase ningún argumento. Esta función puede ser establecida por el usuario mediante ser_new_handler() , pero si no se ha establecido ninguna, entonces new lanza la excepción bad_alloc §3, a la que debería definirse un capturador adecuado.

Ejemplo

try {

 char* ptr;
 for (int i = 0; i <= SIZE; i++) { ptr = new char[10]; }

} catch (const bad_alloc& e) {

 cout << "Memoria agotada " << e.what() << endl;
 abort();

} Observe que what() es un método de la clase bad_alloc El comportamiento descrito corresponde a la última versión del Estándar (de Julio de 1998). Algunas implementaciones antiguas utilizan una convención distinta, que las pautas de las antiguas funciones malloc, calloc y realloc de la librería C clásica. Devuelve siempre un puntero no nulo (distinto de cero) si la operación se realiza con éxito, y un valor cero en caso contrario. Con objeto de permitir que el código escrito conforme a esta última convención pudiese seguir funcionando con los nuevos compiladores, se decidió incluir en estos una versión sobrecargada de los operadores globales que funcionasen al modo antiguo. Son las funciones ::operator new(nothrow) y ::operator new(nothrow)[]. Para que un programa diseñado según el estándar antiguo funcionase correctamente con un compilador que siguiera el nuevo, solo habría que cambiar las sentencias que incluyeran la invocación del operador. Por ejemplo, una versión antigua del código anterior aparecería como: char* ptr; for (int i = 0; i <= SIZE; i++) {

  ptr = new char[10];
  if (!ptr) {    // ptr == 0
    cout << "Memoria agotada" << endl;
    abort();
  }

} Para adaptarla al nuevo estándar habría que transformarla en: char* ptr; for (int i = 0; i <= SIZE; i++) {

  ptr = new (std::nothrow) char[10];
  if (!ptr) {    // ptr == 0
    cout << "Memoria agotada" << endl;
    abort();
  }

}

set_new_handler

Se trata de una función de Librería Estándar, en <new.h>, que permite definir una función que será llamada cuando el operador new no pueda satisfacer la asignación de memoria solicitada. set_new_handler instala la función que será llamada cuando el operador global operator new() u operator new[]() no puedan asignar la memoria requerida. A su vez devuelve un puntero al último manejador si había alguno instalado. Si no existe ninguna función instalada, el operador new lanza por defecto una excepción bad_alloc en caso de no poder asignar la memoria requerida, pero este comportamiento puede ser alterado mediante una llamada a la función set_new_handler para fijar un nuevo manejador.

Sintaxis

new_handler set_new_handler(new_handler mi_puntero);

  new_handler es un typedef  definido en <new.h> como:

typedef void (*new_handler)(); Es decir, un puntero a función que devuelve void que no recibe ningún parámetro. Así pues, la nueva función que instalemos debe tener estas características (ver ejemplo). mi_puntero es un puntero a la función que queremos instalar. Evidentemente será una función que no acepte argumentos y devuelva void. Nota: si se quiere mantener compatibilidad hacia atrás con la versión tradicional de new, que no lanzaba por si mismo ninguna excepción, puede utilizarse un puntero nulo como valor de mi_puntero, mediante set_new_handler(0).

Esquema de funcionamiento

void MiFuncion () { // Función que será invocada en caso de fallo

 cout << "Atención memoria agotada..." << endl;
 exit(EXIT_FAILURE);

} ... {

 new_handler punteroActual;
 new_handler miPuntero = &MiFuncion;
 new_handler miPuntero = MiFuncion;  // alternativa equivalente 
 ...
 punteroActual = set_new_handler(miPuntero);  // MiFuncion queda instalada
 ....
 set_new_handler(punteroActual);     // restituida función anterior

} Si new no puede asignar la memoria solicitada, invoca el manejador que fue establecido en la última llamada a set_new_handler. Si no hubiese instalado ninguno, new devuelve 0. mi_puntero debe especificar las medidas a tomar en tal caso. El manejador mi_puntero definido por el usuario debería tomar alguna de las siguientes acciones: • Liberar memoria suficiente y terminar (return). En este caso new volverá a intentar satisfacer la demanda de memoria, si tiene éxito el programa continuará. Este sería la opción ideal. • Lanzar una excepción bad_alloc o cualquiera derivada de ella. Si no puede adoptarse la primera opción, la función señalada por mi_puntero debe lanzar una excepción que termine el programa, pues de lo contrario se entraría en un bucle indefinido. • Llamar a las funciones abort o exit

Ejemplo

void agotado() {

  cerr << "Memoria agotada\n";
  throw bad_alloc();

} int main() { //

  set_new_handler(agotado);    // M1 Instala manejador
  try {
    for (int i = 0; i <= SIZE; i++) { new char[1000]; }
  }
  catch (bad_alloc) {          // M.5
    cout << "El programa termina con error" << endl;
    exit(1);
 }
 cout << "El porgrama ha terminado con éxito" << endl;

} Comentario La sentencia M1 instala un manejador para caso de fallo del operador new, que permanecerá activo mientras no se instale otro. Observe que en esta sentencia se toma el nombre de la función en sustitución de su puntero. En caso de agotarse la memoria en el bloque try, se invocaría la función agotado que fue instalada en M1. Esta función lanza un mensaje de error, y a continuación una excepción que sería recogida en el manejador instalado en M5. Aquí se lanza un mensaje de aviso y se termina el programa con exit. Una opción preferible es sobrecargar los operadores operator new() y operator new[]() de forma que se comporten de la forma adecuada a las necesidades de nuestra aplicación.

bad_alloc

El objeto lanzado por new cuando se produce un error es del tipo bad_alloc, una clase definida en <new.h> con cuya definición tiene el siguiente aspecto: class bad_alloc : public exception { /* definición-de-la-clase */ }: Como puede verse, al igual que el resto de excepciones lanzados por la Librería Estándar, deriva públicamente de la clase exception. Su interfaz es la siguiente: class bad_alloc : public exception {

 public:
 bad_alloc() throw();                        // constructor
 bad_alloc(const bad_alloc&) throw();       // constructor-copia
 bad_alloc& operator=(const bad_alloc&) throw();  // operador de asignación
 virtual ~bad_alloc() throw();              // destructor
 virtual const char* what() const throw();  // descriptor

};

Véase también

Fuente