tipos sintaxis programas funciones ejemplos dev datos comandos codigos basicos c++ exception memory-leaks constructor

programas - sintaxis de c++



¿El código siguiente causará pérdida de memoria en c++ (7)

class someclass {}; class base { int a; int *pint; someclass objsomeclass; someclass* psomeclass; public: base() { objsomeclass = someclass(); psomeclass = new someclass(); pint = new int(); throw "constructor failed"; a = 43; } } int main() { base temp(); }

En el código anterior, el constructor lanza. ¿Qué objetos se filtrarán y cómo pueden evitarse las pérdidas de memoria?

int main() { base *temp = new base(); }

¿Qué tal en el código anterior? ¿Cómo se pueden evitar las pérdidas de memoria después del lanzamiento del constructor?


Los dos nuevos se filtrarán.

Asigne la dirección de los objetos creados en el montón a punteros inteligentes con nombre para que se elimine dentro del destructor de punteros inteligentes que recibe la llamada cuando se lanza la excepción - ( RAII ).

class base { int a; boost::shared_ptr<int> pint; someclass objsomeclass; boost::shared_ptr<someclass> psomeclass; base() : objsomeclass( someclass() ), boost::shared_ptr<someclass> psomeclass( new someclass() ), boost::shared_ptr<int> pint( new int() ) { throw "constructor failed"; a = 43; } };

Ahora se invocarán los destructores de psomeclass y pinta cuando la pila se desenrolle cuando se lanza la excepción en el constructor, y esos destructores desasignarán la memoria asignada.

int main(){ base *temp = new base(); }

Para la asignación de memoria ordinaria utilizando (no apli- cada) nueva, la memoria asignada por el operador nuevo se libera automáticamente si el constructor lanza una excepción. En términos de por qué molestarse en liberar miembros individuales (en respuesta a los comentarios a la respuesta de Mike B), la liberación automática solo se aplica cuando se lanza una excepción en un constructor de un objeto que se está asignando recientemente, no en otros casos. Además, la memoria que se libera es la asignada para los miembros del objeto, no cualquier memoria que haya asignado dentro del constructor. es decir, liberaría la memoria para las variables miembro a , pinta , objsomeclass y psomeclass , pero no la memoria asignada desde new someclass () y new int () .


Sí, ese código perderá memoria. Los bloques de memoria asignados usando "nuevo" no se liberan cuando se produce una excepción. Esto es parte de la motivación detrás de RAII .

Para evitar la pérdida de memoria, pruebe algo como esto:

psomeclass = NULL; pint = NULL; /* So on for any pointers you allocate */ try { objsomeclass = someclass(); psomeclass = new someclass(); pint = new int(); throw "constructor failed"; a = 43; } catch (...) { delete psomeclass; delete pint; throw; }


Sí, perderá memoria. Cuando se lanza el constructor, no se llamará a ningún destructor (en este caso, no se mostrará un destructor que libere los objetos asignados dinámicamente, pero supongamos que tiene uno).

Esta es una razón importante para usar punteros inteligentes, ya que los poitners inteligentes son objetos de pleno derecho, recibirán destructores durante el desenrollado de la pila de la excepción y tendrán la oportunidad de liberar la memoria.

Si usa algo como la plantilla scoped_ptr <> de Boost, su clase podría verse más como:

class base{ int a; scoped_ptr<int> pint; someclass objsomeclass; scoped_ptr<someclass> psomeclass; base() : pint( new int), objsomeclass( someclass()), psomeclass( new someclass()) { throw "constructor failed"; a = 43; } }

Y no tendría fugas de memoria (y el controlador predeterminado también limpiaría las asignaciones de memoria dinámica).

En resumen (y afortunadamente esto también responde la pregunta sobre el

base* temp = new base();

declaración):

Cuando se lanza una excepción dentro de un constructor, hay varias cosas que debe tener en cuenta en términos de manejar adecuadamente las asignaciones de recursos que pueden haber ocurrido en la construcción abortada del objeto:

  1. el destructor para el objeto que se está construyendo no se llamará.
  2. Destructores para objetos miembros contenidos en la clase de ese objeto se llamarán
  3. la memoria para el objeto que se estaba construyendo será liberada.

Esto significa que si su objeto posee recursos, tiene 2 métodos disponibles para limpiar esos recursos que podrían haber sido adquiridos cuando el constructor lanza:

  1. capte la excepción, libere los recursos, luego vuelva a lanzar. Esto puede ser difícil de corregir y puede convertirse en un problema de mantenimiento.
  2. usar objetos para administrar los tiempos de vida de los recursos (RAII) y usar esos objetos como miembros. Cuando el constructor de su objeto arroja una excepción, se llamará a los objetos miembros y tendrá la oportunidad de liberar el recurso cuyas vidas son las responsables.

Si lanzas un constructor, debes limpiar todo lo que vino antes de la llamada para lanzar. Si estás usando herencia o lanzando un destructor, realmente no deberías. El comportamiento es extraño (no tengo mi estándar a mano, pero podría no estar definido?).


Todo lo que "nuevo" necesita eliminarse, o provocará una pérdida de memoria. Entonces estas dos líneas:

psomeclass = new someclass(); pint = new int();

Causará pérdidas de memoria, porque necesita hacer:

delete pint; delete psomeclass;

En un bloque final para evitar que se filtren.

Además, esta línea:

base temp = base();

Es innecesario Solo tienes que hacer:

base temp;

Agregar el "= base ()" no es necesario.


necesitas borrar psomeclass ... No es necesario limpiar el entero ...

RWendi


Creo que la respuesta principal es incorrecta y aún se pierde la memoria. El destructor para los miembros de la clase no se invocará si el constructor lanza una excepción (porque nunca completó su inicialización, y quizás algunos miembros nunca hayan llegado a sus llamadas de constructor). Sus destructores solo son llamados durante la llamada al destructor de la clase. Eso solo tiene sentido.

Este simple programa lo demuestra.

#include <stdio.h> class A { int x; public: A(int x) : x(x) { printf("A constructor [%d]/n", x); } ~A() { printf("A destructor [%d]/n", x); } }; class B { A a1; A a2; public: B() : a1(3), a2(5) { printf("B constructor/n"); throw "failed"; } ~B() { printf("B destructor/n"); } }; int main() { B b; return 0; }

Con la siguiente salida (usando g ++ 4.5.2):

A constructor [3] A constructor [5] B constructor terminate called after throwing an instance of ''char const*'' Aborted

Si su constructor falla parcialmente, entonces es su responsabilidad tratarlo. Peor aún, ¡la excepción puede ser lanzada desde el constructor de su clase base! La forma de tratar estos casos es empleando un "bloque de prueba de función" (pero incluso entonces debe codificar cuidadosamente la destrucción de su objeto parcialmente inicializado).

El enfoque correcto para su problema sería algo como esto:

#include <stdio.h> class A { int x; public: A(int x) : x(x) { printf("A constructor [%d]/n", x); } ~A() { printf("A destructor [%d]/n", x); } }; class B { A * a1; A * a2; public: B() try // <--- Notice this change : a1(NULL), a2(NULL) { printf("B constructor/n"); a1 = new A(3); throw "fail"; a2 = new A(5); } catch ( ... ) { // <--- Notice this change printf("B Cleanup/n"); delete a2; // It''s ok if it''s NULL. delete a1; // It''s ok if it''s NULL. } ~B() { printf("B destructor/n"); } }; int main() { B b; return 0; }

Si lo ejecuta obtendrá la salida esperada donde solo se destruyen y liberan los objetos asignados.

B constructor A constructor [3] B Cleanup A destructor [3] terminate called after throwing an instance of ''char const*'' Aborted

Todavía puede resolverlo con punteros inteligentes compartidos si lo desea, con copia adicional. Escribir un constructor similar a esto:

class C { std::shared_ptr<someclass> a1; std::shared_ptr<someclass> a2; public: C() { std::shared_ptr<someclass> new_a1(new someclass()); std::shared_ptr<someclass> new_a2(new someclass()); // You will reach here only if both allocations succeeded. Exception will free them both since they were allocated as automatic variables on the stack. a1 = new_a1; a2 = new_a2; } }

Buena suerte, Tzvi.