Alura > Cursos de Programação > Cursos de C e C++ > Conteúdos de C e C++ > Primeiras aulas do curso Orientação a Objetos com C++: trabalhando com herança

Orientação a Objetos com C++: trabalhando com herança

Introdução à herança - Apresentação

E aí, pessoal, boas vindas à Alura. Eu sou o Vinicius Dias e vou guiar vocês nessa segunda parte do treinamento de Programação Orientada a Objetos usando C++ como linguagem.

De novo, a minha intenção aqui não é te ensinar a criar um sistema de banco, obviamente, a minha intenção é te ensinar conceitos de C++. E para termos como exemplo, eu vou continuar o sistema de banco que começamos no último treinamento.

Começando aqui da nossa classe conta, vamos incrementar um pouco ela. Nós vamos aprender sobre herança, que é um dos pilares da Programação Orientada a Objetos; vamos ver sobre métodos virtuais, destrutores virtuais; métodos puramente virtuais, classes abstratas e vários nomes bonitos.

A partir desse monte de nome legal, vamos aprender outros conceitos e outro pilar da Programação Orientada a Objetos. Por exemplo, durante o desenvolvimento de uma funcionalidade muito simples, vamos entender o que é o tal do Polimorfismo, que é uma palavra super bonita, para uma prática bastante simples. Enfim, vamos aprender bastante coisa.

Inclusive, em determinado momento vai precisar sair um pouquinho desse projeto, e fazer alguns rabiscos, fazer alguns rascunhos aqui no código, para entender conceitos mais complexos.

Se alguma dúvida surgir no meio desse processo, não hesite, você pode abrir uma dúvida lá no fórum, que eu tento responder pessoalmente, sempre que possível, mas quando eu não consigo, temos uma comunidade de moderadores, alunos e instrutores bastante solícita, e com certeza alguém vai conseguir te ajudar.

Mais uma vez muitas boas vindas à Alura. Espero que você tire muito proveito desse treinamento e te vejo no próximo vídeo para já começarmos colocando a mão na massa.

Introdução à herança - Criando funcionários

E aí, pessoal, bem vindos de volta. Então, temos aqui no nosso sistema de banco, conta; a conta possui um titular; esse titular possui um CPF. Então já evoluímos bastante o domínio da nossa aplicação.

Para praticarmos um pouco mais, agora vamos precisar modelar um funcionário do nosso banco. Obviamente os bancos têm funcionários, então vamos modelar isso.

Deixa eu fechar isso daqui tudo. E vou aqui no meu SRC criar uma nova classe. O nome dessa classe vai ser funcionário. Já conversamos sobre Namespace, etc, mas por enquanto a gente não vai trabalhar com Namespace aqui. Vamos falar sobre essa parte aqui nesse curso, mas não agora. O resto, vamos deixar tudo da forma como está mesmo.

E vou dar o ok. E temos a nossa definição da classe e a implementação dos métodos que essa classe vai ter. E um funcionário aqui, segundo a abstração do nosso sistema, vai ter um nome, CPF e salário. E nome e CPF já vimos aqui em titular, então, vamos copiar exatamente isso daqui. Vou copiar e colar aqui. Deixa eu remover esse construtor e destrutor porque, por enquanto, não vamos precisar. Só que, obviamente, eu preciso incluir o #include "Cpf.hpp”, e também preciso incluir #include <string>.

Tenho, teoricamente, a nossa definição pronta, só que o meu CPF não vai ter público. Eu deixei público lá no último treinamento para a gente fazer alguns testes, mas ele vai ser privado, assim como o nome.

Deixa eu colocar aqui primeiro. E outra coisa que vamos ver aqui na classe titular, na implementação dela, vamos ver se ela tem algum construtor. E tem aqui no construtor, ela simplesmente faz a inicialização, usando essa lista de inicialização. Então vamos fazer algo semelhante lá na implementação de funcionário.

Deixe eu remover isso aqui. Vamos ter o construtor aqui, que vai receber um CPF, vai receber uma string, que vai ser o nome, e inicializa esses dados. E também vamos precisar verificar o tamanho do nome, vamos precisar fazer isso, mas eu não vou fazer por enquanto.

Repara que a gente já está copiando muito código. Teria um código aqui que ia ficar duplicado, uma regra de negócios. Então, já tem aí a dica de que alguma coisa não está certa. Mas então, vamos lá. Deixa eu fazer essa definição lá na minha definição da classe, no meu arquivo de cabeçalho, que vai estar aqui na parte public. Tenho a definição do meu construtor.

Temos a nossa classe Funcionário pronta para modelarmos funcionário, para criarmos funcionário. Obviamente precisamos de algum getter para recuperar o nome, recuperar o CPF, mas, por enquanto, isso aqui já é o suficiente para criarmos um funcionário.

Só para exemplificarmos como ficaria isso, eu vou incluir aqui o funcionário #include"Funcionario.hpp”. E eu posso, tranquilamente, vir aqui no final, depois de mexer com todas as contas, criar um novo funcionário. E o nome dessa variável vai ser funcionário mesmo.

E quais parâmetros eu preciso passar? Um CPF e um nome. Aqui no CPF eu preciso informar o número do CPF, e o nome vai ser Dias Vinicius. Então eu tenho aqui um funcionário. Teoricamente, tudo está funcionando. Eu vou executar isso daqui só para garantir que não vamos ter nenhum erro. E vamos ver, nenhum erro. Tem aqui um nome muito curto. Em algum momento eu devo ter criado algum titular com o nome muito curto. Tudo está funcionando no nosso sistema, nenhum problema de compilação, nenhum erro.

Só que, então, vamos falar sobre essa duplicação aqui da lista de inicialização que a gente precisou colocar na implementação. Vamos falar sobre a definição que está duplicada de CPF e nome; a gente tem CPF e nome aqui, e tem CPF e nome aqui. Eu acabei me esquecendo de um detalhe aqui, no funcionário colocar o salário; que vai ser um float salario. Ele vai receber também um salário. E lá no nosso construtor eu também preciso informar o salário. float salario.

Mas você já entendeu o que eu quero dizer. Então, basicamente, o que eu quero fazer é, de alguma forma, não precisar duplicar a parte que é incomum. Por exemplo, a validação do tamanho do nome. Eu quero poder utilizar lá no meu funcionário, sem precisar copiar esse código; a inicialização de CPF e nome, eu não quero precisar ficar duplicando.

Então, vamos ver, no próximo vídeo, como que a gente pode começar a estudar uma forma de reaproveitar esse tipo de código.

Introdução à herança - Usando herança

E aí, pessoal, bem vindos de volta. Já vimos que existe código duplicado entre funcionário e titular. Mas a gente sabe também que isso não pode ser uma classe só porque podem existir especificidades. Por exemplo, para o titular eu posso querer armazenar alguma informação do imposto de renda dele, para saber quanto de crédito eu vou dar.

Aqui no Funcionário eu preciso ter o salário dele, calcular bonificação. Eu sei que são tipos diferentes, que eu posso ter informações diferentes, mas que existe algo em algum.

E o que um titular de uma conta, um cliente do nosso banco, e um funcionário, tem em comum? Ambos são pessoas, possuem nome, possuem CPF. Então é exatamente isso que vamos extrair para um tipo novo. Nós vamos modelar, nós vamos criar um tipo Pessoa.

Vamos lá criar uma nova classe para entendermos o que vai acontecer aqui. Então temos uma pessoa. E aqui, esse tipo Pessoa vai ter o quê? Eu vou tirar isso daqui, primeiro eu vou copiar e eu vou colar aqui. Uma pessoa vai ter CPF e vai ter nome. Para isso, obviamente, eu preciso incluir o CPF #include "Cpf.hpp" e preciso incluir #include <string>.

Temos aqui uma pessoa que possui CPF e possui nome, então, ela vai receber aqui como parâmetro um CPF, uma string, que é um nome, e não vai ter destrutor, pelo menos por enquanto. Então vamos lá para a nossa implementação. Eu não preciso do destrutor. Aqui eu preciso receber esses dois parâmetros e a gente simplesmente vai ter uma lista de inicialização. O construtor não vai ter nenhum código em si. Aqui, nessa lista de inicialização, o CPF vai ser o CPF, e o nome vai ser parâmetro nome. Sem segredo nenhum.

O CPF da minha classe, do objeto que está sendo criado, vai receber esse CPF que eu recebi por parâmetro; e nome também. Eu estou pegando por parâmetro e adicionando aqui. Já vimos isso lá no treinamento anterior de listas de inicialização.

Agora o que eu quero informar é que um funcionário é uma pessoa; ele tem tudo que a pessoa tem. Eu quero informar que titular é uma pessoa, então ela tem tudo, um titular tem tudo que uma pessoa tem.

E como que a gente pode fazer isso? Existem algumas formas. Eu poderia, por exemplo, como a gente fez lá com o CPF, vir aqui, informar que um titular possui uma pessoa. Só que isso fica meio estranho, concordam? O titular não tem uma pessoa, ele é uma pessoa.

Para podermos informar isso, para podermos escrever isso aqui, olha só, "um titular é uma pessoa", utilizamos essa sintaxe que eu já vou explicar um pouco melhor. public Pessoa. E aí, claro, eu preciso incluir aqui a pessoa. Eu não preciso mais incluir o CPF. Esse construtor aqui já foi definido lá em pessoa, então ele não vai mais ser necessário. E aqui eu tenho um método privado que verifica o tamanho do nome. De novo, isso aqui devia estar lá na classe Pessoa. Então vamos pegar isso daqui e levar lá para pessoa, que é privado. Inclusive eu vou separar, fazer como eu estava fazendo, separar o, métodos das propriedades, então, vou trazer para cá; e eu vou pegar a implementação lá de titular, de verificar o tamanho do nome, e vou trazer aqui para pessoa. Então a pessoa, verifica tamanho do nome.

Pessoa tem nome, ok; pessoa não conhece std::cout, então deixa eu incluir aqui, #include <iostream>. Agora eu tenho cout, então, a princípio, tudo certo. Uma pessoa possui nome e CPF, sabe verificar o tamanho do nome, então a gente vai precisar fazer isso daqui, verificar o tamanho do nome. Eu posso remover essa implementação verificaTamanhoDoNome().

Repara que meu titular agora não tem mais, por enquanto, nenhuma implementação. Eu vou deixar esse arquivo vazio para, caso a gente precise de algo no futuro, mas, por enquanto, ele é só uma definição de uma especialização de pessoa; e a gente vai falar bem mais sobre isso, pode ficar tranquilo.

Mas aqui, depois de ter inicializado o nome, ou seja, eu defini um nome lá na minha propriedade, nome, eu verifico se o tamanho do nome é válido para garantir que eu não vou poder criar essa pessoa, caso o nome não seja válido.

E agora, vamos ver lá no nosso código da main se a gente está tendo algum erro. E estamos recebendo aqui um erro em Titular. E o que está acontecendo? Não temos um construtor da inicialização de Titular. Então vamos entender o que aconteceu. Vamos aqui em Titular.hpp. E provavelmente você está pensando, "Vinicius, óbvio, você removeu aí o código de titular".

Só que o que acontece? Quando eu digo que Titular é uma pessoa, isso aqui está informando que o titular recebe tudo que a pessoa tem. É como se na hora de criar um titular, eu pegasse tudo que tem lá em Pessoa e colocasse em Titular também.

Eu vou pegar o Cpf cpf, eu vou pegar o nome, eu vou pegar o construtor e vou pegar o verifica tamanho do nome. Só que aqui tem alguns detalhes da linguagem C++. Quando eu tenho uma nova classe, que é o caso aqui de titular, se eu não definir nenhum construtor, o que vai acontecer? Um construtor padrão vai ser criado para mim. Só que, se existe algum construtor criado, e é o caso aqui, de ele receber esse construtor de pessoa, então não temos mais o construtor padrão. Ou seja, o construtor padrão de titular não existe mais, porque ele pegou esse método aqui, que constrói uma pessoa, ele entendeu que é um construtor, então ele precisa ter um construtor aqui também.

De novo, eu vou explicar isso tudo com muito mais calma, mas para isso aqui funcionar, eu vou precisar voltar com o nosso construtor de Titular(). O construtor vai receber por parâmetro um CPF, uma string, que é o nome, e ele só vai inicializar. Então, eu posso fazer a implementação bem simples. Então vamos nessa, deixa eu incluir o Cpf cpf, para garantir que está tudo certo; a string já está incluída. Então vamos lá na nossa implementação de Titular(). Eu vou criar aqui o nosso construtor; que recebe um CPF, que recebe uma string, que é um nome.

E simplesmente vai fazer o quê? Normalmente, o que teríamos seria a inicialização de CPF com CPF, e nome com nome. Só que no nosso caso essa inicialização já é feita lá na classe Pessoa. Então, tudo que eu preciso fazer é isso daqui. E aí eu passo o cpf e o nome. Então agora eu estou com o construtor da minha classe Titular chamando o construtor da classe Pessoa.

De novo, eu já falei várias vezes, mas eu vou falar mais uma vez, isso tudo vai ficar mais claro já já, essa relação entre várias classes, vamos praticar melhor, vai entender melhor. Mas o que eu tenho aqui agora é uma classe Titular sendo, além de um titular, também uma pessoa.

O Titular() pega tudo que pessoa tem e recebe, ele possui aquilo também. Então o titular é uma pessoa. Então, rapidinho, para encerrarmos, vamos fazer também com Funcionário. Então, o funcionário, eu vou precisar incluir aqui #include "Pessoa". Eu vou dizer que um Funcionário é uma Pessoa. Sem segredo.

E se um Funcionário é uma Pessoa, ele já tem o nome e CPF, ele não precisa disso. E agora lá na implementação, o que eu vou fazer? Ao invés de inicializar o nome e CPF, eu vou chamar o construtor de pessoa; que recebe CPF e nome. Só que além de chamar o construtor de pessoa, que faz aquela validação do nome e tudo, eu preciso também inicializar o salário. Então, deixa eu quebrar a linha aqui para você enxergar um pouco melhor.

Eu tenho então o construtor da minha classe Funcionário que recebe um CPF, um nome e um salário, ele está chamando o construtor da classe Pessoa, porque um funcionário é uma pessoa, então ele também precisa inicializar a pessoa.

E além de inicializar tudo que pessoa tem, ele tem um detalhe específico, que é o salário, então ele também está inicializando aqui.

Agora, se nós viermos aqui na nossa main, teoricamente tudo certo, parece que tudo está compilando. Se eu executar isso daqui, vamos ver o que vai acontecer. E deixa eu ver aqui no terminal se está aberto. Aparentemente está tudo funcionando.

Então, no próximo vídeo a gente vai ver alguns detalhes do que pode ter de problema nessa nossa implementação, entender com mais detalhes o que a gente fez, e praticar um pouquinho mais.

Sobre o curso Orientação a Objetos com C++: trabalhando com herança

O curso Orientação a Objetos com C++: trabalhando com herança possui 138 minutos de vídeos, em um total de 58 atividades. Gostou? Conheça nossos outros cursos de C e C++ em Programação, ou leia nossos artigos de Programação.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda C e C++ acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas