Destruidores
Teoria
E aí, aqui é o Juan do Responde Aí, e hoje vamos falar de mais um método especial das classes: os destruidores!
Os métodos destruidores
Da mesma forma que dentro das classes existe um método dedicado a construir os objetos: os construtores, também temos um método dedicado a destruir os objetos! São os chamados destruidores (também são chamados de destrutores ou de destructors).
Assim como os métodos construtores, os destruidores sempre existem. Mesmo quando a gente não escreve um método destruidor, o compilador automaticamente cria um destruidor vazio.
E por quais motivos nós iríamos querer destruir um objeto? O principal motivo de utilizar um método destruidor é liberar memória.
Mas outros usos muito importantes são destruir objetos que estão fora de escopo ou desativar um subsistema do programa que tenha sido inicializado através de uma classe.
Por exemplo, quando a gente cria um objeto dentro de uma função, queremos que ele dure somente dentro da própria função. Ou seja, quando a função acabar, o método destruidor será chamado automaticamente e o objeto será destruído.
Nessa aula, vamos aprender como podemos aproveitar esse processo automático de destruição de objetos pra fazer o C++ realizar outras tarefas úteis!
Para criar um método destruidor basta declarar uma função pública e sem tipo com o mesmo nome da classe, mas usando o símbolo (til) antes do nome:
Uma coisa importante sobre os métodos destruidores é que eles nunca recebem parâmetros, ou seja, os parênteses devem sempre estar vazios!
Vamos ver alguns exemplos. Vamos criar uma classe chamada que contém um método construtor que imprime a mensagem “Criando um objeto!”. E também possui um destrutor que imprime a mensagem “PUFF... objeto sumiu”.
Quando o programa acaba todos os objetos são excluídos da memória e neste momento o método destrutor que criamos é ativado e a mensagem “PUFF... Objeto sumiu!” é exibida. Então, dentro do método destrutor podemos escrever qualquer coisa que a gente queira que aconteça quando o objeto for apagado.
Um processo semelhante acontece quando criamos um objeto dentro de uma função, pois o objeto só existe enquanto a função é executada. Quando o escopo da função acaba, o objeto deixa de existir. Veja no exemplo:
Usando construtores e destrutores para alocação dinâmica de memória
Uma aplicação interessante dos construtores e destrutores é possibilitar uma forma fácil de alocar memória dinamicamente dentro de uma classe.
A gente pode criar uma variável ou array alocada dinamicamente dentro da classe, e usar o destruidor para efetuar a liberação de memória.
Vamos fazer um exemplo. Vamos criar uma classe que usa o construtor para alocar um array dinâmico, e depois usa o destrutor para apagar essa array.
Viu que legal? Isso permite que a gente faça alocações de memória de maneira muito mais segura!
Construtores, destrutores, new e delete. Alocando objetos dinamicamente.
Ok. Mas e seu você quiser alocar um objeto dinamicamente? Os métodos construtores são ativados sempre que um novo objeto é instanciado. E isso se aplica ao uso do operador para alocar um objeto dinamicamente. Já os métodos destruidores são ativados sempre que um objeto é deletado. E isso também se aplica ao uso do operador e aos processos de liberação dinâmica de memória.
Então vamos ao exemplo. Vamos criar um ponteiro para a classe chamado . Em seguida usaremos o operador para alocar um objeto e isso vai ativar o construtor e a mensagem será exibida. Em seguida, com o operador vamos apagar o objeto e isso irá ativar o método destruidor e uma segundo mensagem será exibida.
Uma questão a que você deve se atentar é: quando alocamos um objeto utilizando o comando , então o seu método destrutor só será ativado quando utilizarmos um comando . Isso quer dizer que na linha 26 do exemplo acima se não existisse o comando o método destrutor não seria executado, nem mesmo quando o programa chegasse ao fim.
Boas práticas e evitando erros comuns.
- Não utilize o comando dentro de um destruidor, e nem defina tipos para o método construtor.
- Os métodos destruidores não recebem parâmetros! Mantenha os parêntesis vazios.
- Sempre crie os destruidores em escopo
E por hoje é isso pessoal. Agora que você é quase uma Tartaruga Ninja, vamos fazer exercícios de fixação
Usando construtores e destrutores para alocação dinâmica de memória
Construtores, destrutores, new e delete. Alocando objetos dinamicamente.
Boas práticas e evitando erros comuns.
Exercícios Resolvidos
Exercício Resolvido #1
Elaboração Própria
Descreva o que é um método destruidor e pra que serve?
Passo 1
Vamos lá pessoal.
Um método destruidor é um tipo especial de função que é ativado sempre que um objeto é apagado da memória do computador. O destruidor faz a limpeza da memória, e opcionalmente, o programador pode adicionar outras atividades relevantes como finalizar processos, enviar mensagens pro usuário, e até mesmo deletar outros objetos relacionados.
Um objeto pode ser apagado da memória em basicamente três situações: A primeira situação é quando o escopo do objeto é finalizado, por exemplo, quando um objeto é criado dentro de uma função, e a função acaba. A segunda situação é quando o programador utiliza o comando para excluir o objeto. E a terceira é quando o programa acaba.
Resposta
Ei, a resposta está no passo a passo :)
Exercício Resolvido #2
Elaboração Própria
Um programador está desenvolvendo um sistema de registro de produtos, e você pode ver o código da classe abaixo. O código contém alguns erros, identifique os erros e escreva uma classe corrigida.
Passo 1
E aí pessoal, beleza? Aqui é o Juan, e vamos lá resolver essa questão!
Aqui não tem segredo, a gente precisa analisar o código e encontrar os erros. Então vamos lá.
Dando uma olhada nas linhas de 01 a 04 o cabeçalho aproveita para importar a que será utilizada no restante do código.
Agora vamos olhar a linha 05 onde começamos a nossa classe. De cara, aqui temos o nosso primeiro erro. As classes não recebem parâmetros entre parêntesis!
Passo 2
Seguindo para as linhas 06 a 08 temos a declaração dos atributos da classe. Note que todos estão atributos estão sendo criados em contexto privado, pois este é o padrão quando criamos uma classe no C++.
Mas isso se torna um problema nas linhas 09 a 19, pois os métodos construtor e destrutor devem ser criados em contexto público. Note que este é um erro que pode passar despercebido, pois não gera aviso de erro durante a compilação do programa.
Passo 3
Agora vamos dar uma olhada de perto nos nossos métodos. Da linha 10 a 14 é desenvolvido o construtor, que recebe três parâmetros e utiliza os valores recebidos para preencher os atributos da classe. Aqui não há nenhum erro
Já nas linhas 16 a 19 foi desenvolvido o método destrutor, e aqui temos alguns erros para mencionar.
A própria declaração do método, na linha 16, está errada pois os métodos destrutores não recebem parâmetros, ou seja, os parêntesis devem estar vazios!
Dentro do destrutor uma mensagem é exibida e com isso está tudo certo. Mas na linha 18 temos uma instrução , e o método destrutor não aceita essa instrução.
Com isso a nossa classe está concluída. Mas ainda há mais um erro. Na chave da linha 20 não podemos esquecer do ponto e vírgula
Passo 4
Agora vamos as correções!
Pra começar, vamos remover os parêntesis da linha 05, pois não utilizamos parênteses na declaração de classes.
Agora vamos dar uma olhada no construtor e no destruidor. A primeira mudança é tornar os dois métodos públicos, então adicionamos o na linha 09.
Em seguida, vamos dar atenção ao destruidor. Na linha 16 vamos remover os parâmetros e deixar os parêntesis vazios, e também temos que apagar o comando de dentro do destruidor.
Por último, adicionamos o ponto e vírgula que faltava na linha 20.
E prontinho! Agora corrigimos todos os erros.
Resposta
Linha 05: Parêntesis na classe.
Linha 09: Devemos adicionar um
Linha 16: Funções destrutoras devem ter os parêntesis vazios
Linha 19: Funções destrutoras não devem ter comando
Linha 20: Faltou o ponto e vírgula.
Exercício Resolvido #3
Elaboração Própria
Crie uma classe chamada . Quando um objeto for criado ele deve ser inicializado com um valor que representa o lado. Crie um método que calcule a área deste quadrado. No momento que objeto for destruído uma mensagem informando a área deve ser exibida.
Passo 1
Fala aí, galerinha, vamos resolver essa questão.
Basicamente a questão pediu pra gente criar uma classe que tenha um método construtor que receba um parâmetro, e um método destruidor que exibe uma mensagem para o usuário.
Mas antes, vamos começar com o cabeçalho:
Passo 2
Pronto! Vamos iniciar a construção da nossa classe, chamada
Agora, em contexto privado, vamos definir os atributos da classe. Vamos precisar de um atributo para reter o valor do lado, e um outro atributo para o valor da área:
Passo 3
Vamos começar a desenvolver o construtor: Em um contexto público, definimos uma função sem tipo com o mesmo nome que a classe. Esse método receberá como parâmetro um único valor que representa o lado do quadrado:
Em seguida vamos aplicar o valor recebido no atributo . Agora que temos o valor da variável definido, vamos utilizá-la para calcular o valor da área do quadrado e guardar no atributo
Passo 4
No passo anterior nós definimos o contexto para . Então, o próximo passo é criar o método destruidor.
Primeiros colocamos o símbolo e então definimos uma função sem tipo com o mesmo nome da classe, mas com os parênteses vazios, pois os destruidores não recebem nenhum parâmetro. Não esqueça do pois é ele que indica pro compilador que este é um destruidor.
A seguir, vamos definir o conteúdo do nosso destruidor. Tudo que precisamos fazer é utilizar a instrução para imprimir o valor da área.
E com isso finalizamos a nossa classe
Resposta
Exercício Resolvido #4
Elaboração Própria
Crie uma classe chamada onde se o construtor receber três parâmetros deve-se calcular a solução de uma equação de segundo grau. Crie um destrutor usa os parâmetros e para calcular e imprimir a solução para uma equação de primeiro grau quando a curva cruza o eixo .
Para fins de teste utilize , e .
Passo 1
Vamos nessa galerinha. O que essa questão está pedindo é pra criarmos uma classe com um construtor e um destrutor, onde o construtor calcula uma equação de segundo grau e o destrutor calcula uma equação de segundo grau.
Vamos começar pelo cabeçalho. Para resolver equações de segundo grau vamos precisar incluir a
Passo 2
Vamos começar a nossa classe chamada . Pra começar vamos declarar os atributos da classe.
Todos os atributos estarão privados para garantir a integridade dos dados. Vamos precisar de três atributos e para receber os coeficientes das equações. Note que o atributo só será utilizado quando a equação tiver grau 2.
Também vamos declarar dois atributos e que conterão os resultados da equação. Da mesma forma, o atributo só será usado nas equações de segundo grau
Passo 3
Vamos desenvolver um método do tipo que calcula o valor de quando a reta cruza o eixo das abscissas. Na linha 10 usamos um para exibir a equação para o usuário. Na linha 13 calculamos a raiz da equação e guardamos o resultado no atributo . Para concluir, na linha 14 usamos o para exibir o resultado pro usuário.
Vamos criar um segundo método do tipo que calcula a equação de segundo grau. Na linha 19 utilizamos a instrução para exibir a equação para o usuário.
Na linha 22 calculamos o valor de , note que declaramos uma nova variável dentro da função, pois ela só deve existir durante o cálculo.
Em seguida, usamos uma instrução para determinar quando é impossível obter soluções reais ou quando é possível obter soluções reais .
No primeiro caso, usamos o para informar o usuário. No segundo caso efetuamos os cálculos para encontrar as raízes, e exibimos os resultados.
Passo 4
Agora vamos pro coração da questão e desenvolver nossos métodos construtor e destruidor.
Primeiro de tudo, precisamos mudar o contexto da classe de privado para público utilizando o
O construtor vai receber três valores no momento que um objeto for instanciado, então vamos criar uma função sem tipo com o mesmo nome da classe, e que receba três parâmetros: e . Usaremos letras maiúsculas para não confundir com os atributos que definimos nos passos anteriores.
Em seguida, no corpo do construtor vamos utilizar os valores recebidos para setar os atributos da classe.
Com os valores dos atributos definidos, podemos chamar a função para efetuar o calcula e exibir os resultados pro usuário.
Passo 5
Agora vamos criar o método destruidor. Para declarar o destruidor usamos o operador e criamos uma função sem tipo, com o mesmo nome da classe, que não recebe nenhum parâmetro
No corpo de vamos imprimir uma mensagem informando que este é o destruidor, e em seguida vamos invocar o método , para calcular uma equação de primeiro grau.
E isso conclui a nossa classe.
Passo 6
Antes de finalizar a questão, vamos executar os testes com os valores sugeridos pela questão:
Primeiro vamos criar um objeto chamado que recebe os valores e
O resultado é:
Resposta
Exercício Resolvido #5
Elaboração Própria
Crie uma classe para os empregados de uma empresa. Eles devem ter os atributos privados e , que devem ser inicializados no momento que o objeto seja instanciado. Em seguida, crie um construtor que imprima:
Para fins de teste, instancie um funcionário com nome e salário de
Passo 1
Vamos lá, galera.
A questão quer que a gente crie uma classe que represente os funcionários e essa classe deve possuir um construtor e um destrutor.
Como iremos ter um atributo , será útil inserir a classe para facilitar nossa vida.
Passo 2
Vamos construir a classe chamada , criamos os atributos privados do tipo , e do tipo
Em seguida, vamos pro contexto público da nossa classe e vamos desenvolver o construtor. Criamos uma função sem tipo com o mesmo nome da classe, e esta função receberá os parâmetros e
No corpo do construtor, vamos guardar os parâmetros recebidos nos atributos da classe.
Passo 3
A nossa classe ainda não está concluída. Precisamos desenvolver um destruidor para ela. Tudo que o destruidor fará é exibir a mensagem
Então vamos usar o e criar uma função sem tipo com o mesmo nome da classe. No corpo do destruidor termos um que vai imprimir o nome do funcionário e a frase de demissão.
Ok, a classe está pronta.
Passo 4
Tudo que resta fazer agora é testar a classe.
Pra isso, é só a gente instanciar um objeto dentro da . No momento da criação o construtor será ativado. E quando o programa acabar todos os objetos serão apagados da memória do computador e o destruidor será ativado!
O resultado é:
E prontinho! Nosso programa está concluído!
Resposta
Exercício Resolvido #6
Adaptado de ITA – CES11 – Algoritmos e Estruturas de Dados
Crie uma classe círculo com os atributos que desloca o centro do círculo em e , que aumenta o raio do círculo, e que mostra as informações do círculo. Crie um construtor que defina os dados iniciais do círculo (raio e centro). Defina um destrutor que execute o método . Os atributos da classe devem ser privados.
Passo 1
Vamos lá pessoal.
Essa é uma questão de construtor e destrutor! A questão pede pra gente criar uma classe que tenha um método destruidor e um método construtor, e outros três métodos úteis.
Então vamos começar escrevendo o cabeçalho do código.
Passo 2
Então vamos começar escrevendo a estrutura da classe. Vamos declarar todos os atributos e métodos, e depois a gente desenvolve o conteúdo de cada um deles.
Primeiro, vamos declarar os atributos. Um círculo possui uma posição de seu centro e um valor de raio . Todos devem ser valores reais do tipo .
Logo em seguida, vamos declarar os métodos construtor e destrutor, métodos que não possuem tipo, e têm o mesmo nome da classe. O método destruidor tem o nome começado com o símbolo .
O método construtor vai receber o valor dos atributos e . E o destruidor não recebe nenhum valor.
Em seguida, vamos declarar os demais métodos da classe , e .
Certo. Criamos o esqueleto da nossa classe apenas declarando os protótipos de todos os métodos. Nos próximos passos vamos desenvolver esses métodos.
Passo 3
Vamos desenvolver primeiro a parte mais importante da questão, o construtor e o destrutor.
Primeiro, lembre-se que pra desenvolver um método que já foi declarado, é só a gente chamar esse método usando o operador de escopo
Então vamos pro construtor. Ele é um método sem tipo, que recebe os três parâmetros da classe. Depois, a gente usa o para chamar os atributos da classe e inicializamos com o valor dos atributos.
E o construtor está construído.
Vamos agora dar uma olhada no destruidor. Pra desenvolver um destruidor é só a gente chamar a o método com e mesmo nome da classe: . E dentro do destruidor a gente vai executar o método
E pronto! Não tem dificuldade na hora de criar um destruidor.
Passo 4
O próximo passo é definir os outros métodos da classe.
Primeiro o método . Esse método vai receber como parâmetro um valor de deslocamento para cada uma das componentes. E dentro desse método vamos atualizar os atributos e para adicionar o valor de deslocamento.
Seguindo em frente, vamos desenvolver o método . Esse método vai receber como parâmetro um valor de proporção, e dentro do método vamos multiplicar o valor do raio por essa proporção.
O último método que precisamos desenvolver é o , que não precisa receber nenhum parâmetro. Dentro desse método vamos apenas usar o para imprimir uma mensagem com todos os atributos da classe:
E com isso a gente termina de desenvolver os métodos da classe! A classe está prontinha!
Passo 5
Agora é só a gente testar!
Primeiro, vamos criar um objeto da classe . E vamos executar os métodos e
E agora é só a gente rodar, e olha que legal esse resultado. A gente não executou o método imprimir diretamente, porém o método imprimir está dentro do destruidor então, no momento que o programa acaba o objeto é destruído, e consequentemente o método é executado.
E prontinho! Classe feita e testada.
Resposta
Exercício Resolvido #7
Elaboração Própria
Crie uma classe que tenha como atributo um ponteiro. Essa classe deve ter um método que utiliza este ponteiro para criar um array dinâmico. Se necessário, crie mais métodos e atributos.
Crie também um método que permita acessar qualquer elemento do vetor.
Passo 1
Vamos nessa, pessoal.
Quando tratamos de classes que possuem alocação dinâmica, a gente deve sempre lembrar que uma alocação dinâmica, precisa de uma liberação dinâmica. Ou seja, se usamos o operador vamos ter que usar o operador .
Uma ideia interessante que podemos aplicar é usar o construtor para alocar a memória e depois usar o destruidor para liberar memória. Fazendo desse jeito a gente garante que o vetor dinâmico vai estar sempre disponível dentro da classe, e que quando a classe for destruída, a memória será liberada junto com a destruição da classe.
Então, vamos começar com o cabeçalho da classe:
Passo 2
Então vamos declarar a classe , e dentro da classe vamos declarar o protótipo de todos os métodos.
Primeiro criamos a classe:
E dentro do encapsulamento privado, vamos criar os atributos necessários: o ponteiro que vai armazenar o endereço de memória do array dinâmico, e o atributo que vai armazenar a quantidade de elementos desse array dinâmico.
Nessa questão vamos criar um ponteiro do tipo inteiro. Mas se você quiser, pode criar classes que permitam vetores de qualquer outro tipo!
Agora vamos para o encapsulamento público. Aqui vamos criar o protótipo de todas os nossos métodos
O primeiro método é o construtor, um método sem tipo com o mesmo nome da classe, que vai receber um único parâmetro , pois dentro desse método a gente vai criar a array dinâmica.
O segundo método necessário é o destruidor um método iniciado com , sem tipo, sem parâmetros e com o mesmo nome da classe, que vai ser responsável por liberar a memória.
E o último método será o que vai receber o valor do índice que queremos acessar, e vai retornar o item correspondente.
Pronto. O protótipo da nossa classe está desenvolvido. O próximo passo é desenvolver o conteúdo dos métodos.
Passo 3
Vamos desenvolver o construtor.
O construtor da classe terá três funções.
A primeira função é inicializar o atributo tamanho. O parâmetro do método e o atributo da classe tem o mesmo nome, então vamos usar o para evitar ambiguidades.
A segunda função do construtor é iniciar o array dinâmico. Então, vamos usar o operador para alocar dinamicamente um espaço com o tamanho , e o endereço de memória vai ser guardado no atributo ponteiro .
Prontinho! O array dinâmico foi criado com sucesso. Agora, vamos usar um loop para pedir pro usuário preencher cada uma das posições do vetor:
Passo 4
Agora vamos desenvolver o nosso destruidor.
A função do destruidor vai ser liberar a memória que foi alocada dinamicamente.
Para isso vamos desenvolver o método que vai utilizar o operador para apagar o conteúdo do endereço de memória apontado por .
E é só isso. O destruidor está prontinho!
Passo 5
Já desenvolvemos uma forma de criar e destruir um vetor dinâmico. Mas até agora, não temos nenhuma forma de acessar esse vetor.
Vamos desenvolver o método que vai dar acesso aos itens contidos na array dinâmica. Esse método vai receber o valor do índice do vetor e vai retornar o conteúdo daquele índice:
E prontinho. A classe está concluída.
Passo 6
Agora só falta a gente testar pra garantir que tudo funciona!
Vamos criar um objeto do tipo e vamos inicia-lo com tamanho . Assim que o objeto for criado o construtor vai entrar em ação, e vai pedir que a gente preencha o vetor. Depois vamos exibir o conteúdo de uma posição qualquer.
E pronto, vamos rodar o programa:
E o programa rodou tranquilamente. E veja só a última mensagem: é o destruidor entrando em ação.
Usando uma classe como essa a gente pode criar um montão de vetores dinâmicos sem nos preocuparmos em ficar usando o e o toda vez. É só criar um objeto e pronto!
E por hoje é isso! Até a próxima!
Resposta
Exercício Resolvido #8
Elaboração Própria
Explique o programa abaixo, e diga qual será a saída do programa.
Passo 1
Vamos lá, pessoal.
A questão ta pedindo pra gente ler e interpretar o programa.
A primeira coisa que devemos perceber é a presença da classe . Dentro dessa classe foi declarado um ponteiro para float , e ela possui apenas dois métodos, um construtor e um destrutor.
Vamos dar uma olhada nesse construtor, linhas 07 a 10, a primeira instrução é a impressão de uma mensagem na tela, linha 08. A segunda coisa a ser feita é a alocação dinâmica de um array de 10 posições, linha 09. Em resumo, o construtor da classe cria um vetor dinâmico.
Agora vamos dar atenção ao destrutor, linhas 11 a 14. Ele é bem similar ao construtor da classe, na primeira instrução é impressa uma mensagem, e a segunda instrução é responsável por liberar a memória alocada através do ponteiro.
A gente já debulhou a classe, agora vamos ver o que acontece na .
Na primeira linha da é criado um ponteiro do tipo , linha 18. E logo em seguida esse ponteiro é utilizado para alocar 5 objetos do tipo .
Lembre-se que assim que um objeto é criado o construtor entre em ação. O nosso construtor imprime uma mensagem. Ou seja, na linha 20 são instanciados dinamicamente 5 objetos da classe , logo, nesse exato momento a mensagem “Criando um objeto” é impressa 5 vezes seguidas.
Bem legal! Então vamos continuar.
Na linha 22 a memória alocada dinamicamente é liberada, e nesse momento o destruidor é ativado. E olha só, o destruidor também imprime uma mensagem, então para cada um dos 5 objetos destruídos será impressa a mensagem “Destruindo um objeto”.
E o programa acaba. Então se a gente rodar o programa vamos ter algo desse tipo:
Resposta
Ei, a resposta está no passo a passo :)