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 5

    Herança, Virtual, Override

    Como sabemos, a herança é quando uma classe herda todas as características de outra classe. As classes herdeiras podem, além de herdar as características da classe pai, pode ter atributos e métodos próprios, além de sobreposição de alguns métodos.

    Crie a classe Veiculo novamente com esse código:

    
    class Veiculo {
        public:
            int velMax;
            int rodas;
    
            void imp();
            
            const char* getNome();
            void setNome(const char* nome);
            const char* getCor();
            void setCor(const char* cor);
        private:
            const char* nome;
            const char* cor;
    };
    
    

    Implementação dos métodos, incluindo os getters e setters:

    
    void Veiculo::imp() {
        std::cout << "Nome...: " << this->nome << std::endl;
        std::cout << "Cor....: " << this->cor << std::endl;
        std::cout << "Rodas..: " << this->rodas << std::endl;
        std::cout << "Vel Máx: " << this->velMax << std::endl << std::endl;
    }
    
    const char* Veiculo::getNome() {
        return this->nome;
    }
    
    void Veiculo::setNome(const char* nome) {
        this->nome = nome;
    }
    
    const char* Veiculo::getCor() {
        return this->cor;
    }
    
    void Veiculo::setCor(const char* cor) {
        this->cor = cor;
    }
    
    

    Como visto acima, nada de novo, o método imprimir está dentro da classe e pode acessar atributos privados.

    Agora crie a classe Carro, herdando de Veiculo:

    
    class Carro : public Veiculo {
        public:
            Carro();
    };
    
    

    Implementação do construtor:

    
    Carro::Carro() {
        this->velMax = 160;
        this->rodas = 4;
        this->setNome("Carro");
        this->setCor("Branco");
    }
    
    

    Note que as propriedades chamadas não são da classe Carro, e sim da classe que ela herdou, a Veiculo.

    Da mesma forma, podemos fazer igual em outras classes, como a Moto, que também herda de veículo:

    
    class Moto : public Veiculo {
        public:
            Moto();
    };
    
    

    Implementação do construtor:

    
    Moto::Moto() {
        this->velMax = 200;
        this->rodas = 2;
        this->setNome("Moto");
        this->setCor("Preta");
    }
    
    

    E da mesma forma, a classe Militar, que ganhará algumas propriedades novas:

    
    class Militar : public Veiculo {
        public:
            int municao;
            bool armamento;
            Militar(bool arma, int municao);
    };
    
    

    Implementação do construtor:

    
    Militar::Militar(bool arma, int municao) {
        this->velMax = 150;
        this->rodas = 6;
    
        this->armamento = arma;
        this->setNome("Tanque");
        this->setCor("Verde");
    
        if(arma) {
            this->municao = municao;
        }
        else {
            this->municao = 0;
        }
    }
    
    

    Só que o método imp() não exibirá a munição e o armamento do Militar, para isso, voltamos em veículo e colocaremos o imp() na declaração como virtual, que permitirá sobreescrever o método em outras classes:

    
    class Veiculo {
        public:
            int velMax;
            int rodas;
    
            virtual void imp(); // Todo método que pode ser sobreposto deve ter o virtual na frente.
            
            const char* getNome();
            void setNome(const char* nome);
            const char* getCor();
            void setCor(const char* cor);
        private:
            const char* nome;
            const char* cor;
    };
    
    

    E na classe Militar, colocamos a palavra override no método imp (existente no C++11), que indica que ele está sobrepondo:

    
    class Militar : public Veiculo {
        public:
            int municao;
            bool armamento;
            Militar(bool arma, int municao);
    
            void imp() override;
    };
    
    

    Dessa forma, a implementação do método imp sobreposto em Militar fica assim:

    
    void Militar::imp() {
        std::cout << "Nome.......: " << this->getNome() << std::endl; // Use o getter devido ao encapsulamento
        std::cout << "Cor........: " << this->getCor() << std::endl;
        std::cout << "Rodas......: " << this->rodas << std::endl;
        std::cout << "Vel Máx....: " << this->velMax << std::endl;
        std::cout << "Armamento..: " << this->armamento << std::endl;
        std::cout << "Municao....: " << this->municao << std::endl << std::endl;
    }
    
    

    PS: Pode ser assim também, chamando o método da classe pai junto com o método sobreposto:

    
    void Militar::imp() {
        Veiculo::imp(); // Chamada do método da classe pai
    
        std::cout << "Armamento..: " << this->armamento << std::endl;
        std::cout << "Municao....: " << this->municao << std::endl << std::endl;
    }
    
    

    Em outras palavras, o virtual num método de uma classe pai indica que esse método será sobreposto em alguma classe herdeira. E a classe que sobreescrever o método deverá indicar com override.

    Um método sem o virtual não pode ser sobreposto, de forma que não precisa colocar final num método que não queiramos que seja substituído, apenas deixe ele sem nada (mas pode ser colocado na indicação algo como void nomeDoMetodo() final), para tornar a classe também final, use algo tipo class NomeDaClasse final, o final só pode ser usado em C++11. Todo método que poderá sobreposto deverá ter virtual indicando isso. Em atributos, coloque const na indicação da variável e inicialize dentro da classe mesmo.

    No código principal faça isso:

    
    Carro *c = new Carro();
    
    c->imp();
    
    Moto *m = new Moto();
    
    m->imp();
    
    Militar *t = new Militar(true, 500);
    
    t->imp();
    
    delete c;
    delete m;
    delete t;
    
    

    Lembrando que podemos colocar a tipagem como uma classe (geralmente abstrata), mas a declaração do objeto com new deve ser de uma classe não-abstrata que implemente ou herde os métodos dela, por exemplo:

    
    Veiculo *c = new Carro();
    
    c->imp();
    
    Veiculo *m = new Moto();
    
    m->imp();
    
    Veiculo *t = new Militar(true, 500);
    
    t->imp();
    
    delete c;
    delete m;
    delete t;
    
    

    Isso é útil para ganharmos em polimorfismo, onde podemos usar vários tipos de objetos em, por exemplo, parâmetros, desde que eles tenham a mesma classe pai em comum. Isso é o polimorfismo de inclusão.

    PS: Lembrando que, nesse caso, só poderemos usar os métodos declarados na classe pai, no caso, a Veiculo, mesmo que estes métodos sejam sobrepostos em classes herdeiras.

    Protected x Private x Public

    Já sabemos como utilizar o public e o private

    Criemos uma declaração assim pra exemplificar:

    
    class Veiculo {
        public:
            int rodas;
            const char* nome;
            
        protected:
            int portas;
            const char* cor;
            
        private:
            int velMax;
            int potencia;
    };
    
    

    O public permite o acesso a qualquer parte do programa, o private permite o acesso apenas dentro da própria classe. O protected é meio que um meio-termo, pois permite acesso dentro de classes filhas.

    Veja um exemplo de chamada de métodos protegidos em heranca, numa classe Carro:

    
    class Carro : public Veiculo {
        public:
            Carro();
    };
    
    

    E implementação:

    
    Carro::Carro() {
        this->velMax = 100; // Não podemos acessar
        this->potencia = 150; // Não podemos acessar
        this->rodas = 4;
        this->nome = "Carro";
        this->portas = 4;
        this->cor = "Branco";
    }
    
    

    Como visto, a classe Carro permitiu acesso aos métodos protegidos porque ela herda de Veiculo, uma agregação entre objetos também não vai dar erro:

    
    class Moto {
        public:
            Carro *c;
    };
    
    

    Isso ocorre porque a classe Moto, mesmo não tenho herança, ele cria um elemento Carro dentro da classe.

    Mas se tentarmos acessar qualquer atributo ou método protegido em partes do programa fora da classe e de suas herdeiras, dará erro.