| FAQ Lite | ||||||||||||||||||||||||
| Destrutores | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
| [ 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). |
| [ 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! |
| [ 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] ...
}
| [ 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. |
| [ 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. |
#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
}
| [ 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()
}
| [ 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. |
|
| | Home | Bookmarks | Universidades | Para Saber mais | Universidades | WEB Directory | Mapa do site | | |