c++ - mis - que pasa si borro los datos de facebook
¿Qué pasa si borra[] p falla? (6)
Supongamos que tengo un puntero a una matriz asignada dinámicamente de 10 elementos:
T* p = new T[10];
Más tarde, quiero lanzar esa matriz:
delete[] p;
¿Qué sucede si uno de los destructores T
lanza una excepción? ¿Los otros elementos todavía se destruyen? ¿Se liberará la memoria? ¿Se propagará la excepción o se terminará la ejecución del programa?
Del mismo modo, ¿qué sucede cuando se destruye un std::vector<T>
y uno de los destructores T
lanza?
5.3.5.7 Si el valor del operando de la expresión de eliminación no es un valor de puntero nulo, la expresión de eliminación llamará a una función de desasignación (3.7.3.2). De lo contrario, no se especifica si se llamará a la función de desasignación. [Nota: la función de desasignación se llama independientemente de si el destructor del objeto o algún elemento de la matriz lanza una excepción. - nota final]
No se pudo encontrar nada acerca de los destructores a excepción de
En el caso de una matriz, los elementos se destruirán en orden decreciente de direcciones (es decir, en orden inverso a la finalización de su constructor; ver 12.6.2).
Supongo que después de tirar no se llaman más destructores, pero no estoy seguro.
Bien, aquí hay un código experimental:
#include <cstddef>
#include <cstdlib>
#include <new>
#include <iostream>
void* operator new[](size_t size) throw (std::bad_alloc)
{
std::cout << "allocating " << size << " bytes" << std::endl;
return malloc(size);
}
void operator delete[](void* payload) throw ()
{
std::cout << "releasing memory at " << payload << std::endl;
free(payload);
}
struct X
{
bool throw_during_destruction;
~X()
{
std::cout << "destructing " << (void*)this << std::endl;
if (throw_during_destruction) throw 42;
}
};
int main()
{
X* p = new X[10]();
p[5].throw_during_destruction = true;
p[1].throw_during_destruction = true;
delete[] p;
}
Ejecutar el código dio el siguiente resultado en g ++ 4.6.0:
allocating 14 bytes
destructing 0x3e2475
destructing 0x3e2474
destructing 0x3e2473
destructing 0x3e2472
destructing 0x3e2471
terminate called after throwing an instance of ''int''
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application''s support team for more information.
Por lo tanto, parece que se llama inmediatamente a std::terminate
tan pronto como el primer destructor lanza. Los otros elementos no se destruyen, y la memoria no se libera. ¿Alguien puede confirmar esto?
No puedo verlo explícitamente llamado en el estándar:
Solo que serán llamados en orden inverso de creación.
5.3.5 Eliminar [expr.delete]
6 Si el valor del operando de la expresión de eliminación no es un valor de puntero nulo, la expresión de eliminación invocará el destructor (si existe) para el objeto o los elementos de la matriz que se eliminará. En el caso de una matriz, los elementos se destruirán en orden decreciente de direcciones (es decir, en orden inverso a la finalización de su constructor; ver 12.6.2).
Y que la separación de memoria se realizará incluso si se lanza la excepción:
7 Si el valor del operando de la expresión de eliminación no es un valor de puntero nulo, la expresión de eliminación llamará a una función de desasignación (3.7.4.2). De lo contrario, no se especifica si se llamará a la función de desasignación. [ Nota: la función de desasignación se llama independientemente de si el destructor del objeto o algún elemento de la matriz lanza una excepción. - nota final ]
Probé el siguiente código en G ++ y muestra que no se llaman más destructores después de la excepción:
#include <iostream>
int id = 0;
class X
{
public:
X() { me = id++; std::cout << "C: Start" << me << "/n";}
~X() { std::cout << "C: Done " << me << "/n";
if (me == 5) {throw int(1);}
}
private:
int me;
};
int main()
{
try
{
X data[10];
}
catch(...)
{
std::cout << "Finished/n";
}
}
Ejecutar:
> g++ de.cpp
> ./a.out
C: Start0
C: Start1
C: Start2
C: Start3
C: Start4
C: Start5
C: Start6
C: Start7
C: Start8
C: Start9
C: Done 9
C: Done 8
C: Done 7
C: Done 6
C: Done 5
Finished
Lo que todo conduce de nuevo a esto (respuesta muy antigua):
tirando excepciones de un destructor
Nunca hagas eso. Si ya hay una excepción activa, se llamará std::terminate
: parashift.com/c++-faq-lite/exceptions.html#faq-17.9 . Tu destructor debe. No. Lanzar. Resistirse.
edición: sección relevante de la Norma (14882 2003), 15.2 Constructores y Destructores [except.dtor]
:
15.2.3 El proceso de llamar a los destructores para objetos automáticos construidos en la ruta desde un bloque de prueba a una expresión de lanzamiento se denomina "desenvolvimiento de pila". [Nota: si un destructor llamado durante el desenrollado de pila sale con una excepción, se llama a la terminación ( 15.5.1) . Por lo tanto, los destructores generalmente deben capturar las excepciones y no dejar que se propaguen fuera del destructor. "Nota final"
Testcase para jugar ( en la vida real, lanzar algo que se derive de std::exception
, ¡nunca lanzar int o algo más! ):
#include <iostream>
int main() {
struct Foo {
~Foo() {
throw 0; // ... fore, std::terminate is called.
}
};
try {
Foo f;
throw 0; // First one, will be the active exception once Foo::~Foo()
// is executed, there- ...
} catch (int) {
std::cout << "caught something" << std::endl;
}
}
Para responder a su segunda pregunta, si usó std :: vector en su lugar, no habría ninguna necesidad de eliminar una llamada, no está usando punteros (creo que la clase de vectores es internamente, pero esto no depende de usted) administrar).
Si se lanza una excepción, se lanza. El objeto que no se destruyó, obviamente, no se destruye adecuadamente , y tampoco lo son los que permanecen en la matriz.
Si usa un vector, el problema es el mismo, pero no en su código. :-)
Por lo tanto, lanzar destructores es solo una mala idea (tm).
Como se muestra a continuación en @Martin, el objeto que se arrojó es formalmente inexistente tan pronto como entramos en el destructor. Los otros pueden tener su memoria recuperada también.
Sin embargo, obviamente contenía algunas cosas complicadas que no se debían enrojecer. Si ese objeto, y los otros que lo seguían en la matriz, contenían algunos bloqueos de exclusión mutua, archivos abiertos, cachés de bases de datos o shared_ptrs, y ninguno de ellos tenía sus destructores ejecutados, es probable que tengamos grandes problemas.
¡Tener std :: terminate llamado en ese momento, para sacar al programa de su miseria, parece algo que desearía!