int main()Aquí, con el segundo new una excepción bad_alloc es lanzada. El problema es que la memoria asignada con el primer new nadie la libera. Esto es un memory leak. Una opción para solucionarlo es mediante un bloque finally "a la Java":
{
try {
char *ptr = new char[256];
double *matrix = new double[100000*100000]; // 74 Gigabytes
// ...
delete matrix;
delete ptr;
} catch (const std::bad_alloc& e) {
std::cout << "No hay memoria suficiente" << std::endl;
}
return 0;
}
int main()Claro que esto no hace al manejo de errores una tarea transparente.
{
char *ptr = NULL;
double *matrix = NULL;
try {
ptr = new char[256];
matrix = new double[100000*100000];
// ...
delete matrix;
delete ptr;
} catch (...) { // finally
if (matrix) delete matrix;
if (ptr) delete ptr;
}
return 0;
}
En C++ se puede utilizar la técnica RAII (la adquisición de recursos está en la inicialización), lo que ayuda a evitar leaks. La idea básica detrás de esto es que los constructores obtengan recursos, y los destructores los liberen. Al ocurrir una excepción, se van llamando los destructores de los objetos creados.
Para solucionar nuestro problema, podemos utilizar la clase auto_ptr que se encuentra en <memory>. Un auto_ptr guarda un puntero que es liberado automáticamente en el destructor.
int main()Los deletes no son necesarios. Al salir del ámbito del bloque try, ya sea normalmente o por excepción, se llamará al destructor de los auto_ptrs creados, así no se incurre en ningún tipo de leak.
{
try {
std::auto_ptr<char> ptr(new char[256]);
std::auto_ptr<double> matrix(new double[100000*100000]);
// ...
} catch (const std::bad_alloc& e) {
std::cout << "No hay memoria suficiente" << std::endl;
}
return 0;
}
Advertencia: Los auto_ptr deben ser utilizados sabiendo cuál es su verdadero funcionamiento: La copia de un auto_ptr a otro no los deja equivalentes. La última copia se queda con el puntero, los demás lo van perdiendo, y el delete lo invoca el último auto_ptr destruido. Por ejemplo:
{
std::auto_ptr<Persona> otra_persona;
{
std::auto_ptr<Persona> una_persona(new Persona);
otra_persona = una_persona;
// ahora "una_persona" apunta a NULL
}
} // aquí la persona es destruida
Pregunta: ¿Por qué no usar objetos alojados en el stack en vez del heap para evitar los leaks? Porque el stack es mucho más pequeño. Por ejemplo, el siguiente código provoca un SIGSEGV ya que el arreglo no entra en el stack:
double matrix[1000000];Pero sí entra en el heap:
double *matrix = new double[1000000]; // 7.6 Megabytes