FAQ Lite
Destrutores

[ 11.1 ] O que caracteriza os destrutores?
[ 11.2 ] Em que ordem os objetos locais são destruídos?
[ 11.3 ] Em que ordem os objetos em uma matriz são destruídos?
[ 11.4 ] Posso sobrecarregar o destrutor da minha classe?
[ 11.5 ] Devo chamar explicitamente um destrutor para uma variável local?
[ 11.6 ] O que fazer se eu quero que uma uma variável local morra antes de fechamento ("}") do escopo onde ela foi criada? Posso chamar um destrutor para a variável local se eu realmente quiser?
[ 11.7 ] Ok. Eu não quero chamar explicitamente o destrutor de uma variável local; mas como eu manejo essa situação?
[ 11.8 ] O que fazer se eu não posso empacotar a variável local em um bloco artificial?
[ 11.9 ] Posso chamar explicitamente um destrutor se eu aloquei meu objeto com new ?
[ 11.10 ] O que é placement new e porque eu devo usá-lo?
[ 11.11 ] Quando eu escrevo um destrutor, eu preciso chamar explicitamente os destrutores para os meus objetos membro? 
[ 11.12 ] Quando eu escrevo o destrutor para uma classe derivada, eu preciso chamar  explicitamente o destrutor para minha classe base?
[ 11.1 ] O que caracteriza os destrutores?

Um destrutor providencia o funeral do objeto.

Destrutores são usados para liberar qualquer recurso alocado pelo objeto. Por exemplo, class Lock poderia prender um semáforo, e o destrutor vai liberar esse semáforo. O exemplo mais comum é quando o construtor usa new, e o destrutor usa delete.

Destrutores são funções de "preparação para a morte". São normalmente abreviados para "dtor".

Topo
[ 11.2 ] Em que ordem os objetos locais são destruídos?

Na ordem inversa da construção. O primeiro a ser construído é o último a ser destruído.

No exemplo seguinte, o destrutor de b será executado primeiro, antes que o destrutor de a:

void userCode()
    {
      Fred a;
      Fred b;
      // ...
    } 
Topo
[ 11.3 ] Em que ordem os objetos em uma matriz são destruídos?

Na ordem inversa da construção. O primeiro a ser construído é o último a ser destruído.

No exemplo seguinte, a ordem dos destrutores será
a[9], a[8], ... a[1], a[0]
:

void userCode()
    {
      Fred a[10];
      // ...
    } 
Topo
[ 11.4 ] Posso sobrecarregar o destrutor da minha classe?

Não.

Você pode ter um único destrutor para a classe Fred. Ele sempre se chama

Fred::~Fred()

Nunca recebe parâmetros, e nunca retorna o que quer que seja.

Você não pode, de nenhuma maneira,  passar parâmetros para o destrutor, já que você nunca chama explicitamente o destrutor (ou melhor, quase nunca).

Topo
[ 11.5 ]   Devo chamar explicitamente um destrutor para uma variável local?

Não.

O destrutor seria chamado outra vez no fechamento de } do bloco no qual a variável foi criada. Isso é uma garantia da linguagem; isso acontece automaticamente; não há modo de impedir que isso aconteça. Você pode conseguir resultados realmente catastróficos chamando o destrutor de um objeto duas vezes.

Topo
[ 11.6 ]   O que fazer se eu quero que uma uma variável local "morra" antes de fechamento "}" do escopo onde ela foi criada? Posso chamar um destrutor para a variável local se eu realmente quiser?

Você não pode fazer isso! (Para melhor entendimento, leia, por favor, o tópico anterior desse FAQ).

Suponha que o que se queira, destruindo o objeto local File, seja fechar o arquivo File. Agora suponha que você tem um objeto f pertencente a classe File, e você quer que File f seja fechado antes do fim do escopo do objeto f (isto é, antes do })

    void someCode()
    {
      File f;
    
      // ... [This code that should execute when f is still open] ...
    
      // <— We want the side-effect of f's destructor here!
    
      // ... [This code that should execute after f is closed] ...
    } 
Há uma solução simples para esse problema. Mas por enquanto lembre-se: Não chame explicitamente o destrutor!
Topo
[ 11.7 ] Ok. Eu não quero chamar explicitamente o destrutor de uma variável local; mas como eu manejo essa situação?

Para melhor entendimento, por favor, leia o tópico anterior dessa FAQ.

Simplesmente restrinja a extensão da duração (tempo de vida) da variável local em um bloco artificial {...}:

    void someCode()
    {
      {
        File f;
        // ... [This code will execute when f is still open] ...
      }
    // ^— f's destructor will automagically be called here!
    
      // ... [This code will execute after f is closed] ...
    } 
Topo
[ 11.8 ] O que fazer se eu não posso empacotar a variável local em um bloco artificial?

Na maioria das vezes, pode-se restringir a extensão da duração (tempo de vida) da variável local em um bloco artificial {...}. Mas se por qualquer razão você não tem condições de usar esse artifício, acrescente uma função membro que tenha uma função similar à do destrutor. Mas não chame o destrutor explicitamente!

Por exemplo, no caso de class file, você poderia acrescentar um método close(). Tipicamente o destrutor vai simplesmente chamar esse método close(). Note que o método close() vai precisar marcar o objeto file, de modo que uma chamada subsequente não tente fechar novamente o file já fechado. Por exemplo, ele poderia atribuir ao dado membro fileHandle_ um valor sem sentido, como -1, e poderia verificar, no início do processamento, se fileHandle_  já contém o valor -1:

    class File {
    public:
      void close();
      ~File();
      // ...
    private:
      int fileHandle_;   // fileHandle_ >= 0 if/only-if it's open
    };
    
    File::~File()
    {
      close();
    }
    
    void File::close()
    {
      if (fileHandle_ >= 0) {
        // ... [Perform some operating-system call to close the file] ...
        fileHandle_ = -1;
      }
    } 
Note que outros métodos de File podem também verificar se fileHandle_ é -1, ou seja, se File está fechado.
Topo
[ 11.9 ] Posso chamar explicitamente um destrutor se eu aloquei meu objeto com
new ?

Provavelmente não.

A menos que você use placement new, você deve simplesmente delete o objeto, ao invés de chamar explicitamente o destrutor. Por exemplo, suponha que você alocou o objeto via uma expressão new típica:

Fred* p = new Fred();

O destrutor Fred::~Fred() vai ser chamado automagicamente quando você usar o comando delete.

delete p;  // Automagically calls p->~Fred() 

Você não deve chamar explicitamente o destrutor, porque se fizer isso vai liberar a memória que foi alocada ao próprio objeto Fred. Lembre-se que delete p faz duas coisas: chama o destrutor e desaloca a memória.

Topo
[ 11.10 ] O que é placement new e porque eu devo usá-lo?

Há vários usos de placement new. O mais simples é colocar um objeto em uma determinada localização de memória. Isso é feito fornecendo a localização como um parâmetro pointer para a parte new da expressão new.
    #include <new.h>      // Must #include this to use "placement new"
    #include "Fred.h"     // Declaration of class Fred
    
    void someCode()
    {
      char memory[sizeof(Fred)];     // Line #1
      void* place = memory;          // Line #2
    
      Fred* f = new(place) Fred();   // Line #3 (see "DANGER" below)
      // The pointers f and place will be equal
    
      // ...
    } 
A linha #1 cria uma matriz de sizeof(Fred) bytes de memória, que é grande o suficiente para conter um objeto Fred. A linha #2 cria um pointer place que aponta o primeiro byte dessa memória (Programadores C experientes vão notar que esse passo é desnecessário. Foi incluído aqui apenas para tornar o código mais óbvio). Linha #3 essencialmente apenas chama o construtor Fred::Fred(). O pointer this no construtor de Fred será igual a place. O pointer devolvido f será igual a place.

Advertência: Não use essa sintaxe de placement new a menos que você seja obrigado. Use-a apenas quando você realmente precisar que um objeto seja colocado em uma determinada localização de memória. Por exemplo, quando o seu hardware tem um dispositivo marcador de tempo em uma determinado endereço de memória, e você quer colocar o objeto clock naquela mesma localização.

Perigo: É de sua exclusiva responsabilidade que o pointer que você passa para o placement new operator aponte para uma região de memória grande o suficiente para conter e alinhar adequadamente o objeto que você está criando. Nem o compilador, nem o sistema de execução, farão qualquer ação para verificar se você está certo ou errado. Se sua classe Fred precisa ser alinhada em limite de 4 bytes, mas você forneceu um endereço que não está adequadamente alinhado, você poderá ter um desastre sério nas mãos.

Se você não sabe o que significa alinhamento, por favor, não use a sintaxe placement new. Ok. Você foi avisado.

Você é o único responsável por destruir o objeto criado com placement new. Isso é feito chamando explicitamente o destrutor. 

    void someCode()
    {
      char memory[sizeof(Fred)];
      void* p = memory;
      Fred* f = new(p) Fred();
      // ...
      f->~Fred();   // Explicitly call the destructor for the placed object
    } 
Esse é o único caso em que você chama explicitamente o destrutor.
Topo
[ 11.11 ] Quando eu escrevo um destrutor, eu preciso chamar explicitamente os destrutores para os meus objetos membro? 

Não. Você nunca precisa chamar explicitamente um destrutor, exceto quando usa a sintaxe placement new.

Um destrutor de classe (tenha você o definido explicitamente ou não) automagicamente invoca os destrutores para os objetos membros. Eles são destruídos na ordem inversa em que aparecem dentro da declaração da classe.

    class Member {
    public:
      ~Member();
      // ...
    };
    
    class Fred {
    public:
      ~Fred();
      // ...
    private:
      Member x_;
      Member y_;
      Member z_;
    };
    
    Fred::~Fred()
    {
      // Compiler automagically calls z_.~Member()
      // Compiler automagically calls y_.~Member()
      // Compiler automagically calls x_.~Member()
    } 
Topo
[ 11.12 ] Quando eu escrevo o destrutor para uma classe derivada, eu preciso chamar  explicitamente o destrutor para minha classe base?

Não. Você nunca precisa chamar explicitamente um destrutor, exceto quando usa a sintaxe placement new.

Um destrutor da classe derivada (tenha você o definido explicitamente ou não) automagicamente invoca os destrutores para os sub-objetos da classe base. As classes base são destruídas após os objetos membro. No caso de múltipla herança, as classes base diretas são destruídas na ordem inversa em que aparecem na lista de herança.

    class Member {
    public:
      ~Member();
      // ...
    };
    
    class Base {
    public:
      virtual ~Base();     // A virtual destructor
      // ...
    };
    
    class Derived : public Base {
    public:
      ~Derived();
      // ...
    private:
      Member x_;
    };
    
    Derived::~Derived()
    {
      // Compiler automagically calls x_.~Member()
      // Compiler automagically calls Base::~Base()
    } 
Nota: Ordem de dependência com herança virtual é um caso a parte. Se você está apoiando seu projeto em dependências baseadas em hierarquia de herança virtual, você vai precisar de muito mais informação do que esse FAQ.
Topo Anterior Próximo Índice
C++ FAQ Lite
Copyright © 1991-98 by Marshall Cline Ph.D., cline@parashift.com
Tradução: Dagoberto Haele Arnaut

| Home | Bookmarks | Universidades | Para Saber mais | Universidades | WEB Directory | Mapa do site |