2009-08-20 11 views
5

Estaba debatiendo con algunas universidades sobre lo que sucede cuando se lanza una excepción en una clase asignada dinámicamente. Sé que se llama a malloc y luego al constructor de la clase. El constructor nunca regresa, entonces, ¿qué pasa con el malloc?¿Se libera la memoria cuando lanzo una excepción?

Considere lo siguiente

class B 
{ 
    public: 
     B() 
     { 
      cout << "B::B()" << endl; 
      throw "B::exception"; 
     } 

     ~B() 
     { 
      cout << "B::~B()" << endl;   
     } 
}; 

void main() 
{ 
    B *o = 0; 
    try 
    { 
     o = new B; 
    } 

    catch(const char *) 
    { 
    cout << "ouch!" << endl; 
    } 
} 

¿Qué ocurre con la memoria malloced 'O', ¿tiene fugas? ¿Captura la CRT la excepción del constructor y desasigna la memoria?

Saludos Rich

+0

Respuesta corta: Sí. Ver Cătălin Pitiş Answer para más detalles. –

Respuesta

9

Una llamada a

new B(); 

resuelve en dos cosas:

  • asignación con un operador nuevo() (ya sea el mundial uno o una específica clase, potencialmente, una colocación de uno con la sintaxis new (xxx) B())
  • llamando al constructor.

Si el constructor arroja, se llama al operador correspondiente delete. El caso donde la eliminación correspondiente es una colocación es el único caso donde se llama a un operador de eliminación de ubicación sin la sintaxis :: operator delete(). delete x; o delete[] x; no llame a los operadores de eliminación de ubicación y no hay una sintaxis similar a la colocación nueva para llamarlos.

Tenga en cuenta que si bien se llamará al destructor de B no, los objetos secundarios ya construidos (miembros o B y clases base de B) se destruirán antes de que se elimine la llamada al operador. El constructor que no se llama es el de B.

+0

Adición importante: el constructor para B no se ejecutó, nunca hubo un objeto de ese tipo. Si se llamó a un nuevo() en el constructor de B antes de que se activara la excepción, * no * se está liberando, y usted ya no tiene un puntero a esa memoria, por lo que no puede eliminarlo(). * MANTENGA ALEJADO * desde nuevo() en constructores. – DevSolar

+0

Si usa técnicas RAII (por ejemplo, con std :: auto_ptr), new() en los constructores no es un gran problema. –

+0

@DevSolar: Todo depende de dónde se almacena el resultado de nuevo. Si está en un puntero inteligente, éter local para el constructor o un miembro de la clase B o de sus padres, ese objeto será destruido y la memoria asignada dentro del constructor de B pero antes del lanzamiento de la excepción será liberada. Obviamente si haces un nuevo Foo(); tira "barra", la memoria no será liberada y el Foo no será destruido. En un constructor o en otro lugar. Aclararé que el destructor de B no se llama. – AProgrammer

6

Cuando se produce una excepción desde el constructor, la memoria asignada por el nuevo es liberado, pero el destructor de la clase B no se llama.

+2

Sí. Solo para complementar tu respuesta ... Hay una buena razón para que no se llame al destructor: el objeto nunca se creó. Los destructores son llamados solo por objetos que existieron en un período de tiempo particular. Y esos son los casos en que el constructor se completó con éxito. –

+1

Sí, tienes razón. Un objeto se considera creado (por lo tanto destructible) solo después de que el constructor se ejecute con éxito. –

+0

Nota: se llama destrucotr de cualquier miembro completamente construido y clase (s) base. –

2

En este caso, su objeto, o, en realidad no se construye, y se libera la memoria asignada por la nueva. Como tal, el destructor no se llama. Por lo tanto, NO necesita llamar:

delete o; 

Un patrón de diseño interesante es RAII - La adquisición de recursos es la inicialización. En este patrón, utiliza un constructor para encapsular la adquisición de un recurso y liberar el recurso en el destructor. Si no se puede adquirir el recurso, se agrega el constructor, al igual que en el ejemplo. Por lo tanto, si tiene un objeto válido, tiene el recurso.

Si el objeto está construido, entonces ha adquirido con éxito el recurso. Esto significa que durante la vida del objeto, usted es dueño del recurso. Cuando se elimina el objeto, se libera el recurso. Si el objeto nunca se construye, entonces nunca adquiriste el recurso. Ver Wikipedia:

http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization

+0

En consecuencia, si el constructor ha realizado _algún trabajo de adquisición de recursos antes de lo que desencadena la excepción, es tarea del constructor limpiar antes de lanzar. – Stewart

1

Desde la Norma 5.3 C++ 2003.4/17 - Nuevo:

Si cualquier parte de la inicialización objeto descrito anteriormente termina por lanzar una excepción y una función de cancelación de asignación adecuados se pueden encontrar, la función de cancelación de asignación se llama para liberar la memoria en la que estaba siendo el objeto construido, después de lo cual la excepción continúa propagándose en el contexto de la nueva expresión. Si no se puede encontrar una función de desasignación de asignación que no sea ambigua, la propagación de la excepción no libera la memoria del objeto. [Nota: Esto es apropiado cuando la función de asignación llamada no asigna memoria; de lo contrario, es probable que se produzca una pérdida de memoria. ]

Así que puede o no puede ser un escape - que depende de si un deallocator adecuada se puede encontrar (que normalmente es el caso, a menos que el operador new/delete han sido invalidada) .En el caso en el que hay una desasignador adecuado, el compilador es responsable del cableado en una llamada si el constructor lanza.

Tenga en cuenta que esto está más o menos relacionado con lo que ocurre con los recursos adquiridos en el constructor, que es lo que mi primer intento de respuesta se discutió, y es una cuestión que se debate en muchas preguntas frecuentes, artículos y publicaciones.

+0

Esto estaba destinado a responder la pregunta (¿duplicado?) Aquí: http://stackoverflow.com/questions/1674980/who-deletes-the-memory-allocated-during-a-new-operation-which-has-exception -en –

Cuestiones relacionadas