Pergunta

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();
}

No código acima, o construtor lança. Quais objetos serão vazou, e como podem os vazamentos de memória ser evitado?

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

Que tal no código acima? Como podem os vazamentos de memória ser evitada após o construtor lança?

Foi útil?

Solução

Sim, vai vazar memória. Quando o construtor lança, sem destrutor será chamado (neste caso você não mostrar um destruidor que libera o alocada dinamicamente objetos, mas vamos supor que você tinha um).

Este é um grande motivo para usar ponteiros inteligentes -. Desde os poitners inteligentes são objetos de pleno direito, eles vão ficar destruidores chamados durante o desenrolamento de pilha da exceção e ter a oportunidade de liberar a memória

Se você usar algo como Boost scoped_ptr <> modelo, sua classe poderia olhar mais 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;
    }
}

E você não teria vazamentos de memória (eo dtor padrão também limpar as alocações de memória dinâmica).


Para resumir (e espero que isto também responde à pergunta sobre o

base* temp = new base();

declaração):

Quando uma exceção é lançada dentro de um construtor, há várias coisas que você deve tomar nota em termos de lidar correctamente com as alocações de recursos que podem ter ocorrido na construção abortada do objeto:

  1. o destruidor para o objeto que está sendo construído não ser chamado.
  2. destruidores para membro objetos contidos na classe desse objeto será chamado
  3. a memória para o objeto que estava sendo construída será liberado.

Isto significa que se o objeto possui os recursos, você tem 2 métodos disponíveis para limpar os recursos que já pode ter sido adquiridos quando o construtor lançará:

  1. capturar a exceção, liberar os recursos, então relançar. Isso pode ser difícil de obter correta e pode se tornar um problema de manutenção.
  2. usar objetos para gerenciar as vidas de recursos (RAII) e usar esses objetos como os membros. Quando o construtor para o objeto lança uma exceção, os objetos membros terão desctructors chamados e terão a oportunidade de libertar o recurso cujas vidas eles são responsáveis.

Outras dicas

Ambos os novos do será divulgada.

Atribuir o endereço da pilha criado objetos para nome ponteiros inteligentes para que ele irá ser apagados dentro dos ponteiros inteligentes destruidor que a chamada get quando a exceção é lançada - ( 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;
    }
};

Agora psomeclass e pint destruidores serão chamados quando o desenrolamento de pilha quando a exceção é lançada no construtor, e essas destruidores vai desalocar a memória alocada.

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

Para a alocação de memória normal, usando (não-plcaement) nova, memória alocada pelo operador novo é liberado automaticamente se o construtor lança uma exceção. Em termos de por que se preocupar libertar membros individuais (em resposta a comentários a resposta de Mike B), a libertação automática só se aplica quando uma exceção é lançada em um construtor de um objeto que está sendo new'ly alocado, não em outros casos. Além disso, a memória que é liberado é aqueles alocados para os membros do objeto, e não qualquer memória que você pode ter alocado digamos dentro do construtor. Ou seja, ele iria libertar a memória para as variáveis ??de membro a , pint , objsomeclass e psomeclass , mas não o memória alocada a partir de novo SomeClass () e new int () .

Eu acredito que a resposta de topo é errado e ainda iria vazar memória. O destruidor para os membros da classe irá não ser chamado se o construtor lança uma exceção (porque nunca completado sua inicialização, e talvez alguns membros nunca alcançaram suas chamadas do construtor). Seus destruidores só são chamados durante a chamada destructor da classe. Isso só faz sentido.

Este programa simples demonstra-lo.

#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;
}

com o seguinte resultado (usando g ++ 4.5.2):

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

Se o seu construtor não parcialmente, em seguida, é sua a responsabilidade de lidar com ele. Pior, a exceção pode ser gerada a partir de construtor de sua classe base! A maneira de lidar com estes casos é através do emprego de um "bloco try função" (mas, mesmo assim, você deve cuidadosamente código a destruição de seu objeto parcialmente inicializado).

A abordagem correta para o seu problema, então, seria algo como isto:

#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;
}

Se você executá-lo você vai obter o resultado esperado, onde apenas os objetos alocados são destruídos e liberado.

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

Você ainda pode trabalhar com isso com ponteiros compartilhados inteligentes se você quiser, com cópia adicional. Escrevendo um construtor semelhante a esta:

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;
    }
}

Boa sorte, Tzvi.

Se você jogar em um construtor, você deve limpar tudo o que veio antes da chamada para jogar. Se você estiver usando herança ou jogando em um destrutor, você realmente não deveria ser. O comportamento é estranho (não tenho meu padrão útil, mas pode ser indefinido?).

Sim, esse código vai vazar memória. Blocos de memória alocada usando "novo" não são liberados quando uma exceção é levantada. Esta é parte da motivação por trás RAII .

Para evitar o vazamento de memória, tentar algo como isto:

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;
 }

Tudo o que você precisa "novos" para ser excluído, ou você vai causar um vazamento de memória. Então, estas duas linhas:

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

Será causar vazamentos de memória, porque você precisa fazer:

delete pint;
delete psomeclass;

Em um bloco finally para evitá-los ser vazado.

Além disso, esta linha:

base temp = base();

É desnecessário. Você só precisa fazer:

base temp;

Adicionando o "= base ()" é desnecessário.

você precisa excluir psomeclass ... Não é necessário limpar o inteiro ...

RWendi

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top