Pregunta

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 tira. ¿Qué objetos se filtrarán y cómo se pueden evitar 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?

¿Fue útil?

Solución

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

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

Si utiliza algo como scoped_ptr < > plantilla, 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 pérdidas de memoria (y el dtor predeterminado también limpiaría las asignaciones de memoria dinámica).


En resumen (y espero que esto también responda 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. Los
  3. destructores para los objetos miembros contenidos en la clase de ese objeto se llamarán
  4. se liberará la memoria del objeto que se estaba construyendo.

Esto significa que si su objeto posee recursos, tiene 2 métodos disponibles para limpiar aquellos recursos que podrían haberse adquirido cuando el constructor arroja:

  1. captura la excepción, libera los recursos y vuelve a lanzar. Esto puede ser difícil de corregir y puede convertirse en un problema de mantenimiento.
  2. use objetos para administrar la vida útil de los recursos (RAII) y use esos objetos como miembros. Cuando el constructor de su objeto arroja una excepción, los objetos miembros tendrán llamados desctructores y tendrán la oportunidad de liberar el recurso de cuyas vidas son responsables.

Otros consejos

Ambas novedades se filtrarán.

Asigne la dirección de los objetos creados en el montón a punteros inteligentes con nombre para que se eliminen dentro del destructor de punteros inteligentes que reciben 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 psomeclass & amp; Se llamarán a los destructores pint cuando la pila se desenrolle cuando se arroje 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 (sin ubicación) nuevo, 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 son las asignadas para los miembros del objeto, no cualquier memoria que pueda haber asignado, por ejemplo, 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 () .

Creo que la respuesta principal es incorrecta y aún podría perder memoria. El destructor para los miembros de la clase no se llamará si el constructor lanza una excepción (porque nunca completó su inicialización, y tal vez algunos miembros nunca hayan alcanzado sus llamadas de constructor). Sus destructores solo se invocan durante la llamada del 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 el siguiente resultado (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 a la mitad, entonces es su responsabilidad tratar con él. Peor aún, la excepción puede ser lanzada desde el constructor de su clase base. La forma de tratar estos casos es empleando una función & Quot; try try block & Quot; (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á el resultado esperado 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 este:

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.

Si agrega un constructor, debe limpiar todo lo que vino antes de la llamada a lanzar. Si está utilizando herencia o tirando un destructor, realmente no debería estarlo. El comportamiento es extraño (no tengo mi estándar a mano, ¿pero podría no estar definido?).

Sí, ese código perderá memoria. Bloques de memoria asignados usando & Quot; new & Quot; no se liberan cuando se genera una excepción. Esto es parte de la motivación detrás de RAII .

Para evitar la pérdida de memoria, intente 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;
 }

Todo lo que " nuevo " debe 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 debe hacer:

delete pint;
delete psomeclass;

En un bloque finalmente para evitar que se filtren.

Además, esta línea:

base temp = base();

Es innecesario. Solo tienes que hacer:

base temp;

Agregando " = base () " es innecesario.

necesita eliminar psomeclass ... No es necesario limpiar el entero ...

RWendi

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top