Aprenda C++

  • Página Inicial
  • Contato!
  • Tudo sobre C++ Parte 1!
  • Tudo sobre C++ Parte 2!
  • Tudo sobre C++ Parte 3!
  • Tudo sobre C++ Parte 4!
  • Tudo sobre C++ Parte 5!
  • Tudo sobre C++ Parte 6!
  • Tudo sobre C++ Parte 7!
  • Tudo sobre C++ Parte 8!
  • Tudo sobre C++ Parte 9!
  • Tudo sobre C++ Parte 10!
  • Tudo sobre C++ Parte 11!
  • Tudo sobre C++ Parte 12!
  • Tudo sobre C++ Parte 13!
  • Tudo sobre C++ Parte 14!
  • Tudo sobre C++ Parte 15!
  • Tudo sobre C++ Parte 16!
  • Orientação a Objetos em C++ - Parte 2

    Classe em Arquivo Externo, Construtor e Destrutor

    Dessa vez, crie no projeto, um arquivo de cabeçalho (file). A classe será construída dentro dele, com o mesmo código acima, mas este terá algumas alterações.

    Nele, terá o método construtor, que não tem declaração de tipo e tem o mesmo nome da classe. Esse método construirá automaticamente atributos ao objeto. Assim:

    
    class Aviao {
        public:
            int velocidade;
            int velMax;
            std::string tipo; // Será necessário colocar std em strings, e incluir o iostream no cabeçalho.
    
            Aviao(int tp);
    };
    
    Aviao::Aviao(int tp) {
        if(tp == 1) {
            this->velMax = 800;
            this->tipo = "Jato";
        }
        else if(tp == 2) {
            this->velMax = 350;
            this->tipo = "Monomotor";
        }
        else if(tp == 3) {
            this->velMax = 180;
            this->tipo = "Planador";
        }
       
        this->velocidade = 0;
    }
    
    

    Esse código acima, sempre estará no arquivo de cabeçalho, entre o ifndef/define e o endif.

    Para incluir na página principal, usamos o include entre aspas, no caso #include "Aviao.h". Dessa forma:

    
    #include <iostream>
    #include "Aviao.h"
    
    using namespace std;
    
    int main() {
        Aviao *av[3];
        
        av[0] = new Aviao(1);
        av[1] = new Aviao(2);
        av[2] = new Aviao(3);
    
        delete *av;
    
        return 0;
    }
    
    

    Essa é uma das vantagens da orientação a objetos, já que esse arquivo de cabeçalho pode ser utilizado para criar vários objetos, e também pode ser reaproveitado em outros programas C++.

    Código completo do cabeçalho Aviao.h:

    
    #ifndef AVIAO_H_INCLUDED
    #define AVIAO_H_INCLUDED
    #include <iostream> // Não esqueça do include
    
    class Aviao {
        public:
            int velocidade;
            int velMax;
            std::string tipo;
    
            Aviao(int tp);
            void imprimir();
        private:
    };
    
    Aviao::Aviao(int tp) {
        if(tp == 1) {
            this->velMax = 800;
            this->tipo = "Jato";
        }
        else if(tp == 2) {
            this->velMax = 350;
            this->tipo = "Monomotor";
        }
        else if(tp == 3) {
            this->velMax = 180;
            this->tipo = "Planador";
        }
    
        this->velocidade = 0;
    }
    
    void Aviao::imprimir() {
        std::cout << "Tipo: " << tipo << std::endl;
        std::cout << "Velocidade Máxima: " << velMax << std::endl;
        std::cout << "Velocidade atual: " << velocidade << std::endl;
        std::cout << "-----------------------" << std::endl;
    }
    
    #endif // AVIAO_H_INCLUDED
    
    

    PS: O std nos cout, nos endl e nas strings é porque não estamos informando o namespace no Aviao.h.

    Código na função principal:

    
    #include <iostream>
    #include "Aviao.h"
    
    using namespace std;
    
    int main() {
        Aviao *av[3];
        
        av[0] = new Aviao(1);
        av[1] = new Aviao(2);
        av[2] = new Aviao(3);
    
        av[0]->imprimir();
        av[1]->imprimir();
        av[2]->imprimir();
    
        delete *av;
    
        return 0;
    }
    
    
    

    PS: Podemos criar também classes diretamente no arquivo classes, nesse caso seria algo do tipo:

    No arquivo h, colocamos apenas a parte da assinatura (interface) da classe:

    
    #ifndef AVIAO_H_INCLUDED
    #define AVIAO_H_INCLUDED
    #include <iostream> // Não podemos esquecer do iostream aqui.
    
    // Podemos colocar o using namespace std também aqui, pra evitar o uso de std.
    
    class Aviao {
        public:
            int velocidade;
            int velMax;
            std::string tipo;
    
            Aviao(int tp);
            void imprimir();
        private:
    };
    
    #endif // AVIAO_H_INCLUDED
    
    

    No arquivo cpp, colocaremos a implementação (execução) dos métodos (incluindo os getters, setters e construtores), além dos atributos estáticos:

    
    #include "Aviao.h"
    // Não é necessário colocar iostream e outras bibliotecas aqui, caso estejam em h.
    
    Aviao::Aviao(int tp) {
        if(tp == 1) {
            this->velMax = 800;
            this->tipo = "Jato";
        }
        else if(tp == 2) {
            this->velMax = 350;
            this->tipo = "Monomotor";
        }
        else if(tp == 3) {
            this->velMax = 180;
            this->tipo = "Planador";
        }
    
        this->velocidade = 0;
    }
    
    void Aviao::imprimir() {
        std::cout << "Tipo: " << tipo << std::endl;
        std::cout << "Velocidade Máxima: " << velMax << std::endl;
        std::cout << "Velocidade atual: " << velocidade << std::endl;
        std::cout << "-----------------------" << std::endl;
    }
    
    

    E no método principal, basta incluirmos apenas o arquivo de cabeçalho h normalmente.

    Caso seja necessário chamar um método de uma classe pai dentro de outro método em uma classe filha, chame usando uma sintaxe tipo ClassePai::metodoDela().

    E também podemos fazer sobrecarga de mais de um construtor, colocando na mesma classe dois ou mais construtores, todos com o nome igual da classe e sem tipo, mas com parâmetros diferentes.

    PS: Caso num construtor precise inicializar um atributo como nulo, use NULL e inclua a bibioteca <cstddef>. Lembrando que apenas ponteiros podem usar NULL.

    Além dos construtores, também temos os destrutores em C++, declarados de forma quase igual aos construtores, usando o nome da mesma classe, diferenciando por um til, declarando na classe algo tipo virtual ~NomeDaClasse() (se a classe não for pra extensão, pode retirar o virtual), e a implementação é feita dessa forma:

    
    Aviao::~Aviao() {
        std::cout << "Objeto Destruído" << std::endl; // A mensagem é opcional
    }
    
    

    E para destruir o objeto especificado, fazemos algo assim no main:

    
    delete avi;
    delete *arAv; // Se for destruir array de objetos, coloque o ponteiro pra destruir o array todo
    
    

    PS: Objetos de contâiners como list e vector, só podem ser destruídos separadamente. Podemos fazer um for it pra isso, por exemplo:

    
    for(auto a: av) { // Nome do objeto no lugar do av, o a é o que será iterado.
        delete a;
    }
    
    

    Isso é usado para destruir um objeto no meio da execução do programa, pra não sobrecarregar a memória. Diferente de outras linguagens POO surgidas posteriormente, o C++ não faz a coleta de lixo, por isso é necessário usar o delete pra destruir objetos (objetos agregados em outros objetos são destruídos automaticamente, mas o principal não), na verdade, no C++ a POO é mais primitiva, por ter sido uma das primeiras linguagens a usar esse paradigma. Sempre mantenha o hábito de usar o delete em objetos em C++ por esse motivo.

    Lembrando que, caso tenha agregação de objetos, é recomendado usar o delete this->nomeDoObjeto desses objetos dentro do destrutor da classe, dessa forma:

    
    Aeroporto::~Aeroporto() {
        delete this->avi;
    }
    
    

    PS: Lembrando que o delete só pode ser usado em objetos criados com ponteiros, inicializados com new, por não haver coleta de lixo desse tipo de objeto. Objetos criados sem essa atribuição tem a coleta de lixo feita automaticamente, por isso não se usa delete.

    Após o uso do delete no objeto, podemos atribuir ele novamente com outras características, por exemplo:

    
    Aviao *av = new Aviao(1);
    
    av->imprimir();
    
    delete av;
    
    av = new Aviao(2); // Na nova atribuição é sem ponteiro mesmo.
    
    av->imprimir();
    
    delete av;
    
    

    PS: Não é necessário criar o destrutor para usar o delete, porque ele já cria um por padrão, mas pode ser útil pra exibir mensagens, por exemplo. Os construtores também são criados automaticamente, quando não são especificados no código. Destrutores em C++ não podem ser sobrepostos.

    Métodos e Atributos Estáticos

    No C++ é possível criar métodos estáticos, que é quando podemos chamar um método de uma classe sem precisar criar um objeto, sem usar o new, apenas chamando o nome da classe seguido do método, na sintaxe NomeDaClasse::nomeDoMetodo().

    No código, apenas coloque static antes do tipo da função que deseja ser acessada de tal forma, dentro da classe. O static pode também ser usado em atributos (sempre inicializados da classe), e no código principal chamamos diretamente junto com o nome da classe, como por exemplo NomeDaClasse::nomeDoAtributo = conteudo. (lembrando que nesse caso, mudaremos o conteúdo do atributo da classe e todos os objetos criados por ela serão alterados, por compartilhamento, independente da alteração ser dentro ou fora da classe). Nesse caso, dentro da classe não utilizamos this, só o nome do atributo ou método sozinho.

    Veja um exemplo de uso de métodos e atributos estáticos numa classe denominada Lampada:

    
    // Isso pode ir no arquivo .h:
    class Lampada {
        private:
            static float preco;
            static bool acesa;
        public:
            static void custo();
            static void acender();
            static void apagar();
    };
    
    // Daqui pra baixo iria no arquivo .cpp:
    
    float Lampada::preco = 9.50;
    bool Lampada::acesa = false;
    
    void Lampada::custo() {
        printf("A lâmpada custa R$ %.2f.\n", preco);
    
        // Note que não usamos this para exibir atributos estáticos.
    }
    
    void Lampada::acender() {
        cout << "A lâmpada está acesa!" << endl;
        acesa = true;
    }
    
    void Lampada::apagar() {
        cout << "A lâmpada está apagada!" << endl;
        acesa = false;
    }
    
    

    E no código principal:

    
    int main() {
        Lampada::custo(); // Método estático.
    
        Lampada::acender();
        Lampada::apagar();
    }
    
    

    PS: Não é recomendado chamar atributos e métodos estáticos através de objetos (tanto que algumas linguagens nem permitem isso), pois são atributos e métodos da classe, por isso devem ser chamados diretamente através dela.

    Também podemos fazer novas atribuições em atributos estáticos no código principal, desde que sejam públicos, e toda instância feita por ele também é alterada, por exemplo:

    
    Lampada::preco = 7.25; // Atributo estático, deixe ele público
    
    

    O static, teoricamente, significa que só uma alocação de memória é criada para esse atributo ou método, não tendo duas cópias na memória em simultâneo.

    Em outras palavras, o static manipula os atributos e métodos na classe toda, não apenas em uma instância, e todo objeto criado com ela também terá essa alteração. São atributos e métodos globais.

    PS: Métodos estáticos só podem trabalhar outros métodos e atributos quando estes também forem estáticos, e não podem ser sobrepostos. E atributos estáticos é recomendável eles serem inicializados.