Pergunta básica: quando um programa chama um método destruidor de classe em C ++? Disseram-me que é chamado sempre que um objeto sai do escopo ou está sujeito a umdelete
Perguntas mais específicas:
1) Se o objeto é criado por meio de um ponteiro e esse ponteiro é excluído posteriormente ou recebe um novo endereço para apontar, o objeto para o qual ele estava apontando chama seu destruidor (presumindo que nada mais esteja apontando para ele)?
2) Continuando a questão 1, o que define quando um objeto sai do escopo (não em relação a quando um objeto sai de um determinado {bloco}). Em outras palavras, quando um destruidor é chamado em um objeto em uma lista vinculada?
3) Você gostaria de chamar um destruidor manualmente?
c++
destructor
Pat Murray
fonte
fonte
Respostas:
Depende do tipo de ponteiros. Por exemplo, ponteiros inteligentes geralmente excluem seus objetos quando são excluídos. Ponteiros comuns, não. O mesmo é verdade quando um ponteiro é feito para apontar para um objeto diferente. Alguns ponteiros inteligentes destruirão o objeto antigo ou o destruirão se não houver mais referências. Os ponteiros comuns não têm essa inteligência. Eles apenas mantêm um endereço e permitem que você execute operações nos objetos para os quais eles apontam, fazendo isso especificamente.
Depende da implementação da lista vinculada. Coleções típicas destroem todos os seus objetos contidos quando são destruídos.
Portanto, uma lista vinculada de ponteiros normalmente destruiria os ponteiros, mas não os objetos para os quais eles apontam. (O que pode estar correto. Eles podem ser referências de outros ponteiros.) Uma lista vinculada projetada especificamente para conter ponteiros, entretanto, pode excluir os objetos em sua própria destruição.
Uma lista vinculada de ponteiros inteligentes pode excluir automaticamente os objetos quando os ponteiros são excluídos ou pode fazê-lo se eles não tiverem mais referências. Cabe a você escolher as peças que fazem o que você deseja.
Certo. Um exemplo seria se você deseja substituir um objeto por outro objeto do mesmo tipo, mas não deseja liberar memória apenas para alocá-lo novamente. Você pode destruir o objeto antigo no lugar e construir um novo no lugar. (No entanto, geralmente é uma má ideia.)
fonte
new Foo()
com 'F' maiúsculo.)Foo myfoo("foo")
não é a análise mais irritante, maschar * foo = "foo"; Foo myfoo(foo);
é.delete myFoo
ser chamado antesFoo *myFoo = new Foo("foo");
? Ou então você excluiria o objeto recém-criado, não?myFoo
antes daFoo *myFoo = new Foo("foo");
linha. Essa linha cria uma nova variável chamadamyFoo
, sombreando qualquer uma existente. Embora, neste caso, não haja nenhum, já que omyFoo
anterior está no escopo doif
, que terminou.Outros já trataram dos outros problemas, então vou apenas observar um ponto: você já quis excluir manualmente um objeto.
A resposta é sim. @DavidSchwartz deu um exemplo, mas é bastante incomum. Vou dar um exemplo que está por trás do que muitos programadores de C ++ usam o tempo todo:
std::vector
(estd::deque
, embora não seja usado tanto).Como a maioria das pessoas sabe,
std::vector
alocará um bloco maior de memória quando / se você adicionar mais itens do que sua alocação atual pode conter. Quando ele faz isso, no entanto, ele tem um bloco de memória que é capaz de conter mais objetos do que atualmente no vetor.Para gerenciar isso, o
vector
que nos oculta é alocar memória bruta por meio doAllocator
objeto (o que, a menos que você especifique de outra forma, significa que ele usa::operator new
). Então, quando você usa (por exemplo)push_back
para adicionar um item aovector
, internamente o vetor usa aplacement new
para criar um item na parte (anteriormente) não utilizada de seu espaço de memória.Agora, o que acontece quando / se você
erase
um item do vetor? Ele não pode simplesmente usardelete
- isso liberaria todo o seu bloco de memória; ele precisa destruir um objeto naquela memória sem destruir nenhum outro, ou liberar qualquer bloco de memória que ele controla (por exemplo, se vocêerase
5 itens de um vetor, então imediatamentepush_back
5 mais itens, é garantido que o vetor não será realocado memória quando você faz isso.Para fazer isso, o vetor destrói diretamente os objetos na memória chamando explicitamente o destruidor, não usando
delete
.Se, por acaso, outra pessoa fosse escrever um contêiner usando armazenamento contíguo mais ou menos como um
vector
faz (ou alguma variante disso, comostd::deque
realmente faz), você quase certamente desejaria usar a mesma técnica.Apenas por exemplo, vamos considerar como você pode escrever código para um buffer circular.
Ao contrário dos contêineres padrão, este usa
operator new
eoperator delete
diretamente. Para uso real, você provavelmente deseja usar uma classe alocadora, mas no momento faria mais para distrair do que contribuir (IMO, de qualquer maneira).fonte
new
, você é responsável por chamardelete
. Quando você cria um objeto commake_shared
, o resultanteshared_ptr
é responsável por manter a contagem e chamardelete
quando a contagem de uso chegar a zero.new
(ou seja, é um objeto de pilha).new
.fonte
1) Os objetos não são criados 'por meio de ponteiros'. Existe um ponteiro que é atribuído a qualquer objeto que você 'novo'. Assumindo que é isso que você quer dizer, se você chamar 'delete' no ponteiro, ele irá realmente excluir (e chamar o destruidor) o objeto que o ponteiro desreferenciou. Se você atribuir o ponteiro a outro objeto, haverá um vazamento de memória; nada em C ++ irá coletar seu lixo para você.
2) Estas são duas questões distintas. Uma variável sai do escopo quando o quadro de pilha em que é declarada é retirado da pilha. Normalmente, é quando você sai de um bloco. Objetos em um heap nunca saem do escopo, embora seus ponteiros na pilha possam. Nada em particular garante que um destruidor de um objeto em uma lista vinculada seja chamado.
3) Na verdade não. Pode haver Deep Magic sugerindo o contrário, mas normalmente você deseja combinar suas 'novas' palavras-chave com suas palavras-chave 'deletar' e colocar tudo o que for necessário em seu destruidor para ter certeza de que ele se limpa de maneira adequada. Se você não fizer isso, certifique-se de comentar o destruidor com instruções específicas para qualquer pessoa que esteja usando a classe sobre como eles devem limpar os recursos desse objeto manualmente.
fonte
Para dar uma resposta detalhada à pergunta 3: sim, há (raras) ocasiões em que você pode chamar o destruidor explicitamente, em particular como a contraparte de um novo posicionamento, como observa dasblinkenlight.
Para dar um exemplo concreto disso:
O objetivo desse tipo de coisa é desacoplar a alocação de memória da construção de objetos.
fonte
Ponteiros - ponteiros regulares não suportam RAII. Sem um explícito
delete
, haverá lixo. Felizmente C ++ tem ponteiros automáticos que cuidam disso para você!Escopo - pense em quando uma variável se torna invisível para o seu programa. Normalmente, isso é no final de
{block}
, como você observou.Destruição manual - nunca tente fazer isso. Deixe o osciloscópio e o RAII fazerem a mágica por você.
fonte
std::auto_ptr
está obsoleto no C ++ 11, sim. Se o OP realmente tiver C ++ 11, ele deve usarstd::unique_ptr
para proprietários únicos oustd::shared_ptr
para proprietários múltiplos contados por referência.std::queue<std::shared_ptr>?
Eu descobri quepipe()
entre um produtor e um consumidor encadeado torna a simultaneidade muito mais fácil, se a cópia não for muito cara.Sempre que você usar "novo", ou seja, anexar um endereço a um ponteiro, ou dizer, você reivindicar espaço na pilha, precisará "excluí-lo".
1.sim, quando você exclui algo, o destruidor é chamado.
2.Quando o destruidor da lista encadeada é chamado, seu destruidor de objetos é chamado. Mas se forem ponteiros, você precisará excluí-los manualmente. 3. quando o espaço é reivindicado por "novo".
fonte
Sim, um destruidor (também conhecido como dtor) é chamado quando um objeto sai do escopo se estiver na pilha ou quando você chama
delete
um ponteiro para um objeto.Se o ponteiro for excluído via
delete
, o dtor será chamado. Se você reatribuir o ponteiro sem chamardelete
primeiro, terá um vazamento de memória porque o objeto ainda existe em algum lugar na memória. No último caso, o dtor não é chamado.Uma boa implementação de lista vinculada chamará o dtor de todos os objetos na lista quando a lista estiver sendo destruída (porque você chamou algum método para destruí-la ou ela saiu do próprio escopo). Isso depende da implementação.
Duvido, mas não ficaria surpreso se houvesse alguma circunstância estranha por aí.
fonte
Se o objeto não for criado por meio de um ponteiro (por exemplo, A a1 = A ();), o destruidor será chamado quando o objeto for destruído, sempre que a função onde o objeto se encontra for concluída. Por exemplo:
o destruidor é chamado quando o código é executado para a linha "terminar".
Se o objeto for criado por meio de um ponteiro (por exemplo, A * a2 = new A ();), o destruidor será chamado quando o ponteiro for excluído (exclua a2;). Se o ponto não for excluído explicitamente pelo usuário ou dado um novo endereço antes de excluí-lo, ocorreu o vazamento de memória. Isso é um bug.
Em uma lista encadeada, se usarmos std :: list <>, não precisamos nos preocupar com o desctructor ou vazamento de memória porque std :: list <> terminou tudo isso para nós. Em uma lista vinculada escrita por nós mesmos, devemos escrever o desctrutor e excluir o ponteiro explicitamente. Do contrário, isso causará vazamento de memória.
Raramente chamamos um destruidor manualmente. É uma função que provê o sistema.
Desculpe pelo meu pobre inglês!
fonte
Lembre-se de que o Construtor de um objeto é chamado imediatamente depois que a memória é alocada para esse objeto e que o destruidor é chamado imediatamente antes de desalocar a memória desse objeto.
fonte