AULA 6 - Programação II - Graduação

De IFSC
Revisão de 10h01min de 22 de novembro de 2019 por imported>Fargoud (→‎Exercícios sobre Construtores)
Ir para navegação Ir para pesquisar

CONSTRUTORES

Em uma linguagem de programação qualquer, quando uma variável de um tipo existente, como um inteiro, é declarada, o compilador cria espaço para aquela variável.

Além disso, a linguagem C permite que uma variável seja declarada e inicializada simultaneamente, evitando problemas de utilização de variáveis não inicializadas.

Porém, quando uma variável de um tipo existente sai fora de escopo, o compilador faz com que o espaço para aquela variável seja liberado. Na verdade, o compilador "constrói" e "destrói" a variável.

A linguagem C++ é concebida para fazer tipos definidos pelo usuário (classes). Para permitir uma inicialização direta dos objetos, o compilador necessita de uma função para chamar quando a variável é criada, isto é, o compilador chama um construtor.

Os construtores são muito úteis em tarefas ligadas à inicialização de classes. Nelas pode-se encontrar inicialização direta de membros, alocação dinâmica de memória, recuperação de dados em arquivos, etc.


Atributos não podem ser explicitamente inicializados na declaração da classe – necessidade de métodos de inicialização dos valores dos atributos! Para automatizar este processo ⇒ Construtores.


Construtores – Funções-membro que têm o mesmo nome da classe e são executadas automaticamente e uma única vez! – quando um objeto é criado.

Sintaxe

O construtor é uma função com o mesmo nome da classe e que não pode retornar nenhum valor. Exceto em casos não usuais, o construtor assume que o espaço foi alocado para todos os atributos na estrutura do objeto quando ele é chamado.

class Nome_Classe
{   ...
    Nome_Classe(Lista_Argumentos_inicializ);
    ...
};

Exemplo:

 class DiadoAno
{ private:  int dia, mes, ano;
  public:
    int bissexto();  
    DiadoAno(int d, int m, int a); //protótipo do Construtor
    void VerificaData();
};

Definição do Construtor

 Nome_Classe :: Nome_Classe(Lista_Argumentos_inicializ) 
{  ...
   CORPO DO CONSTRUTOR
   ...
 }

Exemplos:

Classe TestaConstrutor:

#include <iostream>

using namespace std;

class TestaConstrutor{
   public:
 
      TestaConstrutor()
      { cout << "\nCriando novo objeto";}
 
      Mostra() { cout << "\nObjeto ja criado!";}

};

int main()
{ TestaConstrutor t1;
  TestaConstrutor t2;
 
  t2.Mostra();
  cout << "\nHello world!" << endl;
 
  TestaConstrutor t3;

   t1.Mostra();
   t3.Mostra();
   return 0;
}


Classe DiadoAno, agora sem método público de inicialização de dados:

#include <iostream>
#include <string.h>

using namespace std;

class DiadoAno{
 public:
    DiadoAno() {VerificaData();} //não é mais necessário método público
 private:
   int dia;    //1-31
   int mes;    //1-12
   char Mesliteral[10];
   int ano;    //0-2019
   int ndiasmax;
   bool bissexto;
    void VerificaData();
};
 
 void DiadoAno::VerificaData()
{  int diavalido = 0, mesvalido = 0, anovalido = 0;
 
   while(!mesvalido) {
      cout << "\nInforme o mes (1-12)" << endl;    //*********
      cin >> mes;
      if(mes<1 || mes > 12)
         cout<< "\nErro no mes fornecido!!! Entre com um valor entre 1 e 12\n\n";
      else
         mesvalido = 1;
      switch(mes)
        { case 1:  strcpy(Mesliteral, "Janeiro "); ndiasmax = 31; break;
          case 2: strcpy(Mesliteral, "Fevereiro "); ndiasmax = 28; break;
          case 3: strcpy(Mesliteral, "Março ");ndiasmax = 31; break;
          case 4: strcpy(Mesliteral, "Abril ");ndiasmax = 30; break;
          case 5: strcpy(Mesliteral, "Maio ");ndiasmax = 31; break;
          case 6: strcpy(Mesliteral, "Junho "); ndiasmax = 30;break;
          case 7: strcpy(Mesliteral, "Julho "); ndiasmax = 31;break;
          case 8: strcpy(Mesliteral, "Agosto "); ndiasmax = 31;break;
          case 9: strcpy(Mesliteral, "Setembro ");ndiasmax = 30; break;
          case 10: strcpy(Mesliteral, "Outubro ");ndiasmax = 31; break;
          case 11: strcpy(Mesliteral,  "Novembro ");ndiasmax = 30; break;
          case 12: strcpy(Mesliteral, "Dezembro ");ndiasmax = 31; break;
        }
    }//while
 
    while(!diavalido)
    { cout << "\nInforme o dia (1-31)" << endl;
      cin >> dia;
      if(dia <1 || dia>ndiasmax)
         cout<< "\nErro no dia fornecido!!! Entre com o numero maximo de dias para " << Mesliteral << "\n\n";
      else{    diavalido = 1; }
    }
    while(!anovalido)
    {
      cout << "\nInforme o ano (0-2019)" << endl;
      cin >> ano;
      if (ano <0 || ano > 2019)
         cout<< "\nErro no ano fornecido!!! Entre com um valor entre 0 e 2018\n";
      else
         anovalido = 1;
    }
 
   bissexto = (!(ano%4)&&(ano%100)) || (!(ano%400));
   cout << "\nHoje \202 dia:  " << dia ;
   cout << " de " << Mesliteral ;
   cout << " de " << ano << ".\n";
   cout << "\n" << ano << (bissexto? " \202 ":" nao \202 ") << "bissexto. \n\n" ;
}
int main()
{    DiadoAno hoje;    //o construtor é chamado automaticamente aqui
     //hoje.Mostra(); //não há mais chamada de método para a classe!
     cout << "\n\n" << endl;
     return 0;
}

Execução do Construtor

...
void main() // ou outra função, p.e., Button1Click()
{ 
   DiadoAno x(14,6,1992); //cria objeto e passa valores p/ Construtor
   x.ImprimeData();
   x.ImprimeBissexto();
}


Outros exemplos:

#include <iostream>
using namespace std;
/* Classe que representa retangulos */
class retangulo
{  /* Variaveis privadas para representar largura,
   comprimento, area e perimetro do retangulo */
   float larg, comp, area, perim;
   public:
   /* Contrutor da classe 'retangulo', chamado somente na
   declaracao de um objeto do tipo 'retangulo'. Qualquer chamada
   dessa funcao no resto do codigo ira gerar um erro de compilacao
   (nao sera gerado um arquivo executavel) */
    retangulo(float L, float C); //Construtor
   /* Funcao para escrever os valores de 'larg', 'comp',
   'area' e 'perim' na tela */
    void escreve_tela();
};
 /* Definicao do construtor da classe 'retangulo'
 Construtores devem ter o mesmo nome da classe,
 e nao retornam nenhum valor */
 retangulo::retangulo(float L, float C)
{
   /* 'larg' recebe o valor de 'L', que eh a primeira variavel de entrada */
   larg = L;
   /* 'comp' recebe o valor de 'C', que eh a segunda variavel de entrada */
   comp = C;
   /* Formula da area do retangulo */
    area = larg * comp;
    /* Formula do perimetro do retangulo */
    perim = 2 * (larg + comp);
}
/* Definicao da funcao que escreve os valores de
'larg', 'comp', 'area' e 'perim' na tela */
 void retangulo::escreve_tela()
{
       cout << "---------------------------------" << endl;
       cout << "Largura = " << larg << endl;
       cout << "Comprimento = " << comp << endl;
       cout << "Area = " << area << endl;
       cout << "Perimetro = " << perim << endl;
       cout << "---------------------------------" << endl;
}
int main()
{
       /* Criacao de um objeto da classe 'retangulo'
       Os valores entre parenteses serao assinalados
       para as variaveis 'L' e 'C' no construtor da classe */
       retangulo r(2.0, 3.4);
       /*Chamada da funcao que escreve os valores de
       'larg', 'comp', 'area' e 'perim' na tela */
       r.escreve_tela();
       /* A chamada abaixo esta errada. Nao podemos chamar
       o construtor da classe em nenhuma outra parte do
       codigo que nao seja na declaracao de variaveis e objetos.
       Caso voce "des-comente" a linha abaixo, o codigo nao ira compilar*/
       //r.retangulo(10.0, 3.4);
      
       /* Fim do codigo */ 
       return 0;
}


Classe retangulo, agora com um método que permite redefinir atributos iniciais

#include <iostream>
using namespace std;
/* Classe que representa retangulos */
class retangulo
{
     /* Variaveis privadas para representar largura,
      comprimento, area e perimetro do retangulo */
      float larg, comp, area, perim;        
      public:
     /* Contrutor da classe 'retangulo', chamado somente na
      declaracao de um objeto do tipo 'retangulo'. Qualquer chamada
      dessa funcao no resto do codigo ira gerar um erro de compilacao
     (nao sera gerado um arquivo executavel) */
      retangulo(float L, float C);
      /* Outro contrutor da classe 'retangulo', chamado somente na
      declaracao de um objeto do tipo 'retangulo'. Qualquer chamada
      dessa funcao no resto do codigo ira gerar um erro de compilacao
      (nao sera gerado um arquivo executavel) */
      retangulo(); 
       /* Funcao para definir os valores de 'larg', 'comp', 'area' e 'perim',
       de acordo com os valores de entrada 'L' (para a largura) e 'C'
       (para o comprimento do retangulo)*/
       void define_ret(float L, float C);    
       /* Funcao para escrever os valores de 'larg', 'comp',
       'area' e 'perim' na tela */
       void escreve_tela();
 }; 
  /* Definicao do construtor da classe 'retangulo'
  Construtores devem ter o mesmo nome da classe,
  e nao retornam nenhum valor */
  retangulo::retangulo(float L, float C)
 {
        /* 'larg' recebe o valor de 'L', que eh a primeira variavel de entrada */
       larg = L;
        /* 'comp' recebe o valor de 'C', que eh a segunda variavel de entrada */
       comp = C;
        /* Formula da area do retangulo */
       area = larg * comp;
        /* Formula do perimetro do retangulo */
       perim = 2 * (larg + comp);
  }

   /* Definicao de outro construtor da classe 'retangulo'
   Este construtor ser· chamado quando um objeto da classe
   'retangulo' for declarado sem nenhum valor de entrada,
   Nesse caso, vamos definir 'larg', 'comp', 'area' e 'perim'
   iguais a zero. Construtores devem ter o mesmo nome da classe,
    e nao retornam nenhum valor */
   retangulo::retangulo()
 {
      larg = comp = area = perim = 0;
 }

  /* Definicao da funcao que define os valores de 'larg', 'comp',
  'area' e 'perim', de acordo com os valores de entrada 'L'
   (para a largura) e 'C' (para o comprimento do retangulo)*/
   void retangulo::define_ret(float L, float C)
   {
   /* 'larg' recebe o valor de 'L', que eh a primeira variavel de entrada */
      larg = L; 
    /* 'comp' recebe o valor de 'C', que eh a segunda variavel de entrada */
      comp = C;
     /* Formula da area do retangulo */
      area = larg * comp;
      /* Formula do perimetro do retangulo */
      perim = 2 * (larg + comp);
 }

 /* Definicao da funcao que escreve os valores de
 'larg', 'comp', 'area' e 'perim' na tela */
  void retangulo::escreve_tela()
 {
       cout << "---------------------------------" << endl;
       cout << "Largura = " << larg << endl;
       cout << "Comprimento = " << comp << endl;
       cout << "Area = " << area << endl;
       cout << "Perimetro = " << perim << endl;
       cout << "---------------------------------" << endl;
 }

 int main()
 {
    /* Criacao de um objeto da classe 'retangulo'
    Os valores entre parenteses serao assinalados
    para as variaveis 'L' e 'C' no primeiro construtor da classe */
    retangulo r1(2.0, 3.4);
    /* Criacao de outro objeto da classe 'retangulo'
    Como nao ha nenhum valor de entrada, sera chamado o segundo
    construtor, 'retangulo()' */
    retangulo r2;
      
    /*Chamada da funcao que escreve os valores de
    'larg', 'comp', 'area' e 'perim' na tela. Como 'r2'
     foi declarado pelo segundo construtor, todas as suas
     variaveis devem estar iguais a zero. */
     cout << "---------------------------------" << endl;
     cout << "r2 no inicio do codigo:\n";
     r2.escreve_tela();  
     /*Chamada da funcao que define os valores de
     'larg', 'comp', 'area' e 'perim' para 'r2'. */
      r2.define_ret(10, 20);
      /*Chamada da funcao que escreve os valores de
      'larg', 'comp', 'area' e 'perim' na tela. Agora, 'r2'
      tera valores novos definidos para as suas variaveis */
      cout << "r2 atualizado:\n";
      r2.escreve_tela();
     /*Chamada da funcao que escreve para 'r1' os valores de
     'larg', 'comp', 'area' e 'perim' na tela */
      cout << "r1:\n";
      r1.escreve_tela();        
     /* Fim do codigo */
      return 0;
}


Outro exemplo de Classe DiadoAno com Construtor Classe DiadoAno com Construtor

Exercícios sobre Construtores

  1. Escreva a classe ItemLoja, contendo os campos Nome, PrecoUnit e Quantidade. Crie construtores que pegam os valores dos atributos na criação dos objetos, ou os solicitam ao usuário. No código cliente, crie um vetor de 100 objetos dessa classe. Sugestão de solução: Classe Item Solução
  2. Projete a classe Cliente abaixo:
    PRG2cliente.png
  3. Expanda a classe acima para um vetor contendo 100 Clientes, e registrando o valor da venda diária da loja.
#include <iostream>
#define NCLIENTESMAX 4

using namespace std;

class ClientesLoja{
private:
    char Nome[80];
    char Endereco[100];
public:
   ClientesLoja();
   void AlteraDados();
   void ObterCompra();

} Clientes[NCLIENTESMAX];

ClientesLoja::ClientesLoja()
{    
} 
void ClientesLoja::AlteraDados()
{
   cout << "\n\n\t\tNOME:";
   cin >> Nome;
   cout << "\n\n\t\tENDERECO:";
   cin >> Endereco;
}

int NClientesAtualLoja = 0;

void CadastrarNovo();
void AlterarDados();
void MostrarLista();
void RegistrarCompra();
void Faturamento();

int main()
{  char opcao;
   cout << "*************************" << endl;
   cout << "*** PROGRAMA DA LOJA*****" << endl;
   cout << "*************************" << endl;
   for(;opcao!='s' && opcao!='S';)
   {
       cout << "*** MENU *****" << endl;
       cout << "\n[C]adastra novo cliente: ";
       cout << "\n[A]ltera dados do cliente: ";
       cout << "\n[M]ostra lista de clientes: ";
       cout << "\n[R]egistra nova compra; ";
       cout << "\n[F]aturamento do dia; ";
       cout << "\n[S]air ";
       cout << "\nEscolha sua opcao: ";
       cin >> opcao;
       switch(opcao)
       {
       case 'c':
       case 'C':
                CadastrarNovo(); break;
       case 'a':
       case 'A':
  //              AlterarDados(); break;
       case 'm':
       case 'M':
  //              MostrarLista(); break;
       case 'r':
       case 'R':
  //              RegistrarCompra(); break;
       case 'f':
       case 'F':
  //              Faturamento(); break;
       case 's':
       case 'S':
                cout << "\n\t\tSaindo..."<< endl; break;
       }
   }
   return 0;
}

void CadastrarNovo()
{
  Clientes[NClientesAtualLoja].AlteraDados();
  NClientesAtualLoja++;
}

SOBRECARGA DE CONSTRUTORES

É possível, e recomendável, criar-se dois ou mais códigos distintos ao mesmo Construtor - garantir mais de uma forma de inicialização de parâmetros ao usuário, por exemplo.

Para tanto ⇒ incluir os diferentes protótipos na declaração da classe e em seguida, definir os diferentes códigos.

Exemplo:

#include <iostream>
using namespace std;

class TestaConstrutor{
  public:
      TestaConstrutor();
      TestaConstrutor(int m, int a);
      TestaConstrutor(int d, int m, int a);
      void Mostra() { cout << "\nObjeto ja criado!";}
  private:
      int dia;
      int mes;
      int ano;
      };

   TestaConstrutor::TestaConstrutor()
      { cout << "\nCriando novo objeto";
        cout << "\nEntre com o dia: ";
        cin >> dia;
        cout << "\nEntre com o mes: ";
        cin >> mes;
        cout << "\nEntre com o ano: ";
        cin >> ano;
        }

   TestaConstrutor::TestaConstrutor(int d, int m, int a)
      { cout << "\nCriando novo objeto";
      dia = d;
      mes = m;
      ano = a;
        cout << "\nDia ";
        cout << dia;
        cout << "\nMes: ";
        cout << mes;
        cout << "\nAno: ";
        cout << ano;
        }

      TestaConstrutor::TestaConstrutor(int m, int a)
      { cout << "\nCriando novo objeto";
      mes = m;
      ano = a;
      cout << "\nEntre com o dia: ";
      cin >> dia;
        cout << "\nDia ";
        cout << dia;
        cout << "\nMes: ";
        cout << mes;
        cout << "\nAno: ";
        cout << ano;
        }

int main()
{ TestaConstrutor t1;
  TestaConstrutor t2(9,2011);
  t2.Mostra();
   cout << "\nHello world!" << endl;
   TestaConstrutor t3(23, 4, 2000);
   t1.Mostra();
   t3.Mostra();
   return 0;
}

class Data
{   ...
  Data(int d, int m, int a); //Construtor
  Data( ); //Construtor sobrecarregado
    ...
};
//Definição dos construtores:
Data::Data(int d, int m, int a)
{   ValidaData(d, m, a);
}
//
Data::Data( )
{   int d, m, a;
    cout << “\n Dia: “; cin >> d;
    cout << “\n Mês: “; cin >> m;
    cout << “\n Ano: “; cin >> a;
    ValidaData(d, m, a);
}

Execução da classe:

void main( ) // ou outra função
{     
   Data x(14, 6, 1992), z; //cria dois objetos: x e z
   x.ImprimeData( );
   x.ImprimeBissexto( );
   z.ImprimeData( );
   z.ImprimeBissexto( );
}
Execconstrutores.png

Exercício 2

Expanda agora a classe DiadoAno para que esta tenha um construtor padrão, com os 4 argumentos, e um sobrecarregado, para nenhum argumento.


Sugestão de solução: Classe DiadoAno com Construtor Sobrecarregado

OBSERVAÇÕES SOBRE CONSTRUTORES

1. Um construtor pode dar DIRETAMENTE valores default aos atributos, na declaração da classe

Ex:

class Horario
{ private:
    int hora, minuto, segundo;
  public:
    Horario( )  //Construtor
    { hora=minuto=segundo= 0; } //inic. direta de atribs.
    void SetaHora(int, int, int);
    void ImprimeMilitar( );
    void ImprimePadrao( );
};


2. Em C/C++ os construtores não podem ser chamados explicitamente pelo programador

Contra-ex:

int main( )
{ Horario h; 
  ...
  Horario( ); //ERRO!!!
  ...
}

3. É permitido definir-se um construtor de corpo vazio - na prática: impede que outro construtor seja executado automaticamente para o objeto. Ex:

      ... 
      Horario( ) { }
      ...

4. Construtores com argumentos default - outra alternativa para a proibição de se inicializar atributos em uma classe

Construtores default podem ser invocados em parâmetros (na verdade já definidos). Porém só poderá haver um construtor default por classe! Ex:

class Horario
{ public: 
   Horario(int = 0, int = 0, int = 0); //Construtor default
    ...
 };

Corpo do construtor default:

Horario::Horario(int h, int m, int s)
{   SetaHora(h, m, s);  }

Execução do construtor default:

void main()
{ Horario h1, h2(2), h3(21,34), h4(12,25,42);
  ...
}
Execconstrutores02.png


Contra-Ex:

class Horario
{ public: 
  Horario(int = 0, int = 0, int = 0); 
  Horario ( ); //ERRO !!!
  ...
};

Exemplo:

Classe DiadoAno com construtor default:

#include <iostream>
#define ANOATUAL 2018
using namespace std;
//Declaração da classe DiadoAno:
class DiadoAno
{  public:
     DiadoAno(int =1, int =1, int =2018,int =2000);
   private:
    int dia;    //1-31
    int mes;    //1-12
    int ano;    //0-2017
    int idade;
    int anonasc;
    void VerificaData();
    char * mesextenso;
};
//Função "usuária" da classe - main:
int main()
{ DiadoAno hoje(07,3,2018, 2010), amanha, depoisamanha(13,2);
      return 0;
 }
//Construtor da classe DiadoAno:
DiadoAno::DiadoAno(int d, int m, int a,int nasc)
{    dia = d;
     mes = m;
     ano = a;
     anonasc = nasc;
    VerificaData();
}
//Método PRIVADOVerificaData() da classe DiadoAno:
void DiadoAno::VerificaData()
{    int ehbissexto=0, diamax, dataok=1;
    if(dia <1 || dia>31)
    {
         cout<< "\nErro no dia fornecido!!!\n\n";
         dataok= 0;
    }
    if (ano <0 || ano > ANOATUAL)
    {
        cout<< "\nErro no ano fornecido!!! Entre com um valor entre 0 e " << ANOATUAL << "!\n\n";
        dataok =0;
    }
   if(   (!(ano%4)&&(ano%100)) ||  (!(ano%400)))
         ehbissexto = 1;
   idade = ANOATUAL - ano;
   switch(mes)
      { case 1:  mesextenso= " Janeiro ";
                       diamax = 31;
                       break;
        case 2: mesextenso=" Fevereiro " ;
                       diamax = ehbissexto? 29: 28;
                       break;
        case 3: mesextenso=" Março ";
                      diamax = 31;
                      break;
        case 4: mesextenso= " Abril ";
                       diamax = 30;
                       break;
        case 5: mesextenso= " Maio ";
                       diamax = 31;
                       break;
        case 6: mesextenso=" Junho ";
                       diamax = 30;
                       break;
        case 7: mesextenso= " Julho ";
                       diamax = 31;
                       break;
        case 8: mesextenso=" Agosto ";
                       diamax = 31;
                       break;
        case 9: mesextenso=" Setembro ";
                      diamax = 30;
                      break;
        case 10: mesextenso= " Outubro ";
                         diamax = 31;
                         break;
        case 11: mesextenso= " Novembro ";
                         diamax = 30;
                          break;
        case 12: mesextenso=" Dezembro ";
                         diamax = 31;
                         break;
        default: cout<< "\nErro no mes fornecido!!! Entre com um valor entre 1 e 12\n\n";
                      dataok = 0;
                      break;
      }
   if (dataok && (dia<=diamax))
   {  idade = ano - anonasc;
        cout << "\nHoje \202 dia: " << dia << " de " << mesextenso << " de " << ano << ".\n";
        cout << "Voce completa "<< idade << " anos de idade , no ano de " << ano <<  "\n";
        if(ano != ANOATUAL)
            cout << "\nE completara"<< (ANOATUAL - anonasc) << " anos de idade , no ano de " << ANOATUAL <<  "\n\n\n";
        if(ehbissexto) cout << "\n O ano de " << ano << " \202 bissexto! \n";
   }
   else
        if(dia>diamax)
           cout << "\nO mes de "<< mesextenso << "so tem " << diamax << " dias!!!\n DATA INVALIDA!\n\n\n";
        else
           cout<< "\n DATA INVALIDA!\n\n\n";
}

DESTRUTORES

Quando um objeto sai fora do seu escopo, um destrutor pode ser chamado.

Embora a liberação de memória seja uma aplicação bastante freqüente para funções destrutoras, esta não é sua única utilidade. Uma segunda utilidade para funções destrutoras pode ser a finalização de dispositivos ou subsistemas que tenham sido ativados como classes.

Por exemplo, ao terminar a impressão pode-se finalizar a impressora desativando eventuais modos de impressão ajustados pelo sistema. Outra utilidade para a função destrutora, às vezes associada ao gerenciamento dinâmico de memória, está na prática de gravar os dados ao se encerrar o escopo dos mesmos (ou antes de liberá-los da memória).

Se o programador não proporcionar construtores (pode haver mais de um) e um destrutor (só pode haver um) para uma classe, o compilador assume as ações mais simples. Em outras palavras, caso não seja definido um construtor e um destrutor para a classe, o compilador cria um construtor e um destrutor default, sem código e sem parâmetros, que são chamados, respectivamente, a cada declaração de instância, e cada vez que a instância sai fora do escopo.

O nome do destrutor é o da classe com um til (~) anexado no início. O destrutor nunca pega nenhum argumento; ele só é chamado pelo compilador e não pode ser chamado explicitamente pelo programador.

Como geralmente o desejado é fazer vários tipos de inicialização num objeto, o "destrutor padrão" (fazendo nada) é muitas vezes suficiente e não é necessário definir um destrutor.

Em resumo:

  • São métodos que liberam a memória alocada para o objeto.
  • Também utilizam o mesmo nome da classe, mas precedido por um ‘~’ (til).
  • Tal como o construtor, o destrutor não pode ter valor de retorno e nem pode ser chamado explicitamente.
  • Ao contrário dos construtores, Destrutores não aceitam argumentos e nem podem ser sobrecarregados.

Exemplo:

class CriaEDestroi
{  public: 
   CriaEDestroi (int);
   ~CriaEDestroi( );
   private:
   int numero;
};
//
CriaEDestroi::CriaEDestroi(int valor)
{   numero = valor;
    cout << “Construtor do objeto ” << numero;  }
//
CriaEDestroi::~CriaEDestroi( )  //sem tipo e sem argumentos
{    cout << “ Destrutor do objeto  “ << numero << endl;  }


Entretanto, se um objeto inicializa algum hardware ou muda algum valor global, pode ser preciso desfazer o efeito do objeto quando este é destruído. Para isso precisa-se de um destrutor.

Exemplo de Execução:

CriaEDestroi primeiro(1);  // cria um objeto global
//
int main( )
{   cout << “(global criado antes de main) “ << endl;
  CriaEDestroi segundo(2); //cria objeto local
  cout << “(local em main) “ << endl; 
  cria( );
  CriaEDestroi terceiro(3); //outro objeto local
  cout << “(outro local em main)” << endl;	
  return 0;
};
//
void cria  (void)
{  CriaEDestroi  quarto(4);
   cout << “(local em cria)” << endl;  }
Execconstrutores3.jpg
#include <iostream>
using namespace std; 
/* Classe generica */
 class generica
{
  /* String para identificar cada objeto */
  string titulo;
  public:
  /* Declaracao do construtor para a classe 'generica' */
  generica(string s); 
  /* Declaracao do destrutor para a classe 'generica' */
  ~generica();
};
/* Definicao do construtor da classe 'retangulo'
Construtores devem ter o mesmo nome da classe,
e nao retornam nenhum valor */
generica::generica(string s)
{
       titulo = s;
       cout << "------------------------------------------------\n";
       cout << "Construcao do objeto --- " << titulo << endl;
       cout << "------------------------------------------------\n";
       cout << endl;
 }

/* Definicao do destrutor da classe 'retangulo'
 Destrutores devem ter o mesmo nome da classe,
 antecedidos de um '~', e nao retornam nenhum valor */
 generica::~generica()
{
       cout << "------------------------------------------------\n";
       cout << "Destruicao do objeto --- " << titulo << endl;
       cout << "------------------------------------------------\n";
       cout << endl;
}

int main()
{
     /* Criacao de tres objetos da classe 'generica'.
     Cada objeto tera uma string diferente para sua
     variavel 'titulo' */
     generica OBJ1("PRIMEIRO OBJ"), OBJ2("SEGUNDO OBJ"), OBJ3("TERCEIRO OBJ");
     cout << "\n------------------------------------------------\n";
     cout << "Instrucoes no meio do codigo de main()\n";
     cout << "------------------------------------------------\n\n";
     /* Fim do codigo da funcao 'main()'*/
     return 0;
     /* Apos o 'return' acima, os destrutores dos
      objetos 'OBJ1', 'OBJ2' e 'OBJ3' serao chamados*/
}
  /* Aplicacao pratica de destrutores - desalocar memoria dinamica */
  #include <iostream>
  using namespace std;
  /* Classe que representa vetores inteiros N-dimensionais */
  class vetor
  {
       public:
       /* Tamanho do vetor */
       int N;
       /* Ponteiro que indicara as N posicoes do vetor de inteiros */
       int *vet;
       /* Construtor da classe*/
       vetor(int tamanho);
       /* Destrutor da classe */
       ~vetor();
       /* Funcao para escrever o vetor na tela */
       void escreve_tela();
 };

 /* Construtores da classe 'vetor' */
 vetor::vetor(int tamanho)
{
       N = tamanho;
       vet = new int [N];
}

/* Destrutor da classe 'vetor'. Com ele, nao precisamos
 nos preocupar em desalocar a memoria de 'vet' */
 vetor::~vetor()
{
       N = 0;
       delete [] vet;
       cout << "-------------------------------------------------\n";
       cout << "Destruicao de um vetor - desalocando memoria\n";
       cout << "-------------------------------------------------\n";
}

/* Funcao para escrever na tela o vetor
'vet' da classe 'vetor' */
void vetor::escreve_tela()
{
       int j;
       for(j=0;j<N;j++)
               cout << vet[j] << " ";
       cout << endl;
}

int main()
{
       int j;
       int tam = 10;
       /* Criacao de dois objetos do tipo 'vetor'.
       Ambos terao 'tam' posicoes. */
       vetor A(tam), B(tam);

       /* Definicao dos valores dos vetores.
       Valores escolhidos somente para ilustracao.*/
       for(j=0; j< tam; j++)
       {
               A.vet[j] = 2*j;
               B.vet[j] = (tam-j)*3;
       }
      
       /* Visualizar na tela os valores dos vetores */
       cout << "\n------------------------------------------------\nA = ";
       A.escreve_tela();
       cout << "------------------------------------------------\n\n\n";
       cout << "------------------------------------------------\nB = ";
       B.escreve_tela();
       cout << "------------------------------------------------\n\n";
      
       /* Fim do codigo da funcao 'main()'*/
       return 0;
       /* Apos o 'return' acima, os destrutores dos
       objetos 'A' e 'B' serao chamados*/
}

Exercícios

  1. Crie a classe Retangulo, o qual contém os atributos privados x1, y1, x2 e y2 (coordenadas do ponto superior esquerdo e do ponto inferior direito, respectivamente), os métodos privados Calcula_Area, Calcula_Perimetro e o método público Desenha.

Teste, para esta classe, os seguintes métodos construtores:

DICA: você pode utilizar os métodos ShowMessage(), para mandar mensagens ao usuário e a propriedade Canvas (com os métodos MoveTo(x,y) e LineTo(x,y) do formulário ou de um quadro de imagem, para traçar o retângulo.



<< Programação orientada a eventos e gráfica AULA 6 - Construtores e Destrutores Separando a interface da implementação >>

<<= Página do curso