Polimorfismo
Teoria
E aí, aqui é o Juan do Responde Aí, e agora vamos falar de mais um pilar da orientação a objetos, o Polimorfismo.
O que é polimorfismo?
Polimorfismo é um desses conceitos que são mais difíceis de explicar do que de fazer.
Polimorfismo significa “muitas formas”, e é exatamente isso que ele significa em C++. O polimorfismo é a capacidade de um objeto ou método assumir formas diferentes.
Mas o que isso quer dizer? Quando fazemos uma classe filha ela herda os métodos de uma classe mãe. Mas as vezes, um método herdado não é adequado, ou deveria agir de outra forma para uma certa classe filha específica.
A solução é usar os polimorfismos para escrever um novo método, porém usando o mesmo nome do método herdado. O usuário ainda pode usar o mesmo método, porém, sem saber que por baixo dos panos, o comportamento do método é diferente!
Ou seja, de maneira prática, o polimorfismo em C++ é a capacidade de reescrever um método que foi herdado.
Mas qual a vantagem do polimorfismo? A vantagem é que podemos escrever uma classe mãe com um método genérico bem simples que funcione com a maioria das classes filhas. E pras classes filhas que tiverem algo de especial, podemos usar polimorfismos para adaptar o conteúdo da classe mãe.
O polimorfismo é parecido com a sobrecarga que já estudamos. Mas existe uma diferença essencial: enquanto a sobrecarga representa variações de um método, o polimorfismo é a substituição de um método herdado, por um novo método com mesmo nome.
Métodos virtuais
Antes de a gente falar dos polimorfismos, a gente precisa conhecer mais uma palavra-chave do C++ o . Essa palavra-chave deve ser escrita nos membros da classe mãe que vão sofrer polimorfismo:
Usar a palavra não é obrigatório. Porém, é aconselhável utiliza-lo em todos os métodos em que você pretende aplicar polimorfismo
O deixa o código mais fácil de ler e entender, e facilita o uso dos ponteiros com classes que usam polimorfismo, e em situações em que um método faz chamada para um outro método que sofre polimorfismo. Usar a palavra-chave deixa claro pro programador e pro compilador que esse método sofrerá polimorfismo e será reescrito em classes filhas.
Usando polimorfismo
Para executar um polimorfismo é só escrever dentro da classe filha um método com o nome igual ao do método que está sendo substituído. Vamos fazer um exemplo bem simples.
O primeiro passo é criar uma classe mãe que tem um método virtual. No exemplo, criamos a que tem o método virtual , esse vai ser o método que sofrerá polimorfismo.
Dentro da vamos executar um polimorfismo. Para isso, é só a gente escrever um novo método com o mesmo nome e pronto! O polimorfismo está feito.
É como eu falei, fazer polimorfismo é mais fácil do que explicar polimorfismo. E pra usar as classes é só criar objetos e utilizar os métodos normalmente:
Viu só. A classe mãe e a classe filha possuem um método com o mesmo nome mas que efetuam atividades diferente!
Classes abstratas
Uma coisa interessante dos métodos virtuais é que eles podem ser completamente vazios. São os métodos virtuais puros, que são declarados colocando um “” na declaração do método:
Classes que possuem pelo menos um método virtual puro são chamadas de classes abstratas. E se todos os métodos da classe forem virtuais puros, então temos um interface.
Duas coisas vão acontecer quando você criar uma classe abstrata:
- Você não pode instanciar um objeto de uma classe abstrata. Isso gera um erro!
- Uma classe herdeira de uma classe abstrata deve ter todos os métodos virtuais sobrescritos através de polimorfismo.
Ou seja, você não consegue usar uma classe abstrata diretamente, porque o propósito das classes abstratas é servir de protótipos para criar classes filhas concretas. As classes abstratas servem de régua pra criar classes derivadas que sigam um determinado padrão.
Por outro lado, uma classe concreta é uma classe que é útil diretamente. Todas as classes que nós já fizemos até aqui eram classes concretas.
Exemplo de polimorfismo
Vamos ver agora um exemplo mais complexo de um programa de cadastro de funcionários.
A gente criou uma classe mãe , que tem os atributos , e e o método, e criamos três classes filhas , e :
O método simplesmente utiliza o atributo , o que funciona sem problemas nas classes e .
Porém, a remuneração do vendedor é composta de duas coisas: o salário mensal fixo mais uma comissão sobre o valor vendido. A solução é usar polimorfismo na classe filha .
Primeiro vamos fazer um código simples para a classe mãe . Repara no detalhe: a função vai sofrer polimorfismo. Então, deve ser uma função virtual. A primeira etapa é utilizar o nos métodos que sofrerão polimorfismos.
Em seguida, vamos desenvolver a classe que herda os membros de . Para efetuar uma herança a gente usa o operador e escreve
Em seguida, dentro da classe vamos adicionar o atributo :
E adicionar o método que recebe um parâmetro que corresponde ao valor vendido, atualiza o atributo e exibe uma mensagem pro usuário.
Ok! Até aqui tudo que fizemos foi herdar uma classe e adicionar novos membros na classe filha. Agora, vamos executar o polimorfismo! Executar o polimorfismo é só a gente reescrever o método virtual dentro da classe filha.
O novo método vai primeiro calcular uma comissão de para o vendedor. E depois vai imprimir o salário e o também a comissão!
E pronto! É só isso! A gente executou o polimorfismo!
Se aprofundando nos detalhes do Polimorfismo.
- O uso de métodos virtuais é recomendado, mas não é obrigatório!
- Métodos virtuais geram herdeiros virtuais.
- Criar um destrutor virtual é uma boa prática quando fazemos alocação dinâmica.
É perfeitamente possível aplicar polimorfismo em um método comum, ou seja, em um método que não tem a palavra . Porém, usar métodos virtuais facilita o trabalho com os ponteiros e em casos que um método da classe mãe executa um método com polimorfismo.
Um outro ponto é que quando um método virtual é herdado, todos os polimorfismos deste método serão considerados virtuais (mesmo sem usar a palavra chave ).
Isso quer dizer que um método virtual permanecerá virtual em todas as classes herdeiras e descendentes.
E é isso pessoal! Agora é só fazer uns exercícios de fixação.
Métodos virtuais
Usando polimorfismo
Classes abstratas
Exemplo de polimorfismo
Se aprofundando nos detalhes do Polimorfismo.
Exercícios Resolvidos
Exercício Resolvido #1
Fonte: Elaboração própria
O que é polimorfismo? Como executamos um polimorfismo em C++?
Passo 1
O polimorfismo é a capacidade de um objeto ou método assumir formas diferentes. Isso é, quando um mesmo nome pode ser usado para coisas diferentes, dependendo do contexto.
No contexto de herança, o polimorfismo é a capacidade de uma classe filha substituir um método herdado da classe mãe, por um novo método com comportamento diferente e mesmo nome. Ou seja, o polimorfismo é a substituição de um método herdado, por um novo método com mesmo nome.
No C++ a gente executa um polimorfismo simplesmente escrevendo um novo método dentro da classe filha que tenha o mesmo nome de um método já existente na classe mãe. Para executar um polimorfismo é só escrever dentro da classe filha um método com o nome igual ao do método que está sendo substituído
Uma característica interessante do C++ são os métodos virtuais, que são métodos que desde o começo são pensados para sofrer polimorfismo. Para criar um método virtual é só usar a palavra chave
Resposta
Ei, a resposta está no passo a passo :)
Exercício Resolvido #2
Fonte: Elaboração própria
O que é um método virtual? Qual a diferença entre uma classe concreta, classe abstrata e interface.
Passo 1
Um método virtual é um método que foi pensado desde o começo para sofrer polimorfismo. Para criar um método virtual basta utilizar a palavra-chave no momento de declarar o método. Usando métodos virtuais nós facilitamos a leitura do código, e facilita o uso de ponteiros com classes derivadas.
Um método virtual funciona como um método comum, recebe parâmetros, efetua operações e pode retornar um resultado.
Porém, é possível criar métodos virtuais completamente vazios. Esses são os métodos virtuais puros. Os métodos virtuais puros não efetuam nenhuma atividade, e servem simplesmente para “guardar espaço” numa classe mãe. Esse método deve obrigatoriamente sofrer polimorfismo.
Uma classe que possui ao menos um método virtual puro é chamada de classe abstrata. Classes abstratas não podem gerar objetos. Se todos os métodos de um classe forem virtuais puros, então é chamado de interface. As classes abstratas e interfaces são desde o começo pensadas para funcionar como classes bases para gerar classes filhas que serão utilizáveis.
Caso uma classe não possua nenhum método virtual puro, então ela é chamada de classe concreta. Essas são as classes que podem ser utilizadas diretamente gerando novos objetos.
Resposta
Ei, a resposta está no passo a passo :)
Exercício Resolvido #3
Fonte: Elaboração própria
Observe a classe abaixo. Use herança e polimorfismo para criar a classe
A classe acima utiliza o cabeçalho
Passo 1
Vamos nessa, pessoal.
A questão já foi bacana com a gente e entregou a classe mãe prontinha! Então tudo que temos que fazer é criar a classe filha.
A classe mãe representar um vetor de duas dimensões, e já possui as coordenadas e e o método para calcular o módulo desse vetor.
Para criar uma classe para vetores de três dimensões a gente pode herdar a classe e ganhamos sem esforço algum os atributos e , e a gente só vai precisar adicionar o atributo .
Já o método terá de sofrer polimorfismo. Afinal de contas, na classe mãe tanto a mensagem exibida quanto o cálculo só funcionam para vetores de duas dimensões. Então vamos usar polimorfismo para substituir esse método para uma versão tridimensional.
Mas primeiro, vamos escrever o cabeçalho da questão, que vai precisar incluir o
E também já vamos copiar o código da classe mãe pra dentro do nosso código:
Passo 2
Certo. Vamos agora desenvolver a nossa classe.
Primeiro, vamos declarar a classe que herda a classe . Lembre-se para criar uma classe com herança é assim:
Beleza! Agora nós temos os atributos e e o método a nossa disposição. O próximo passo é adicionar o atributo :
Show! Agora, a gente tem que lembrar que o método não é adequado para vetores de três dimensões. Então vamos executar polimorfismo para adaptar o método.
Para executar polimorfismo não tem segredo. É só a gente escrever um novo método com nome dentro da nossa classe filha!
Então, dentro de vamos escrever a classe . Nesse método “polimorfado” a gente vai imprimir uma mensagem utilizando os três atributos , e . E em seguida, vamos escrever uma equação que calcule o módulo de um vetor tridimensional.
E prontinho! A classe filha e o polimorfismos estão feitos.
Passo 3
Agora é só a gente testar pra ver se deu tudo certo:
Pra testar, é só a gente criar um objeto de cada classe.
Vamos preencher os atributos do vetor bidimensional e executar o método módulo:
E fazer a mesma coisa com o vetor 3D:
E prontinho! Rodando o código vemos que deu tudo certo:
Resposta
Exercício Resolvido #4
Adaptado de: Univasf – Prof. Leonardo Barreto Campos – Polimorfismo
Observe a classe
Crie uma classe derivada de chamada que possui os atributos e . Na classe crie um construtor para a classe e métodos getters para os atributos, implemente o método que mostre os valores de todos os atributos.
Passo 1
E aí, beleza?! Vamos resolver esse exercício!
A questão entregou pra gente uma classe e pede pra gente criar uma filha. A nova classe terá novos métodos e atributos.
Mas a chave da questão é que a classe filha vai ter um novo método chamado , ou seja, vamos executar polimorfismo neste método.
Mas antes de começar, vamos escrever a classe da questão dentro do nosso código:
Vamos analisar esse código antes de prosseguir. A classe possui um atributo protegido e dois métodos públicos.
O atributo é chamado que, podemos presumir, vai armazenar o nome do paciente.
O primeiro método é simplesmente um getter que apenas retorna o valor do atributo .
E o segundo método é o que imprime um texto com os dados do paciente, nesse caso, apenas o atributo nome. Note que esse é um método virtual, isso quer dizer que ele foi pensado desde o começo para sofrer polimorfismo.
Passo 2
Vamos começar criando a classe herdando a classe base . Lembre-se, a gente executa uma herança assim:
No código fica assim:
Agora os atributos e métodos da classe mãe foram copiados para a classe filha.
Em seguida, vamos adicionar na classe filha os seus novos atributos: uma string que armazena o nome da seguradora e o inteiro que armazena o número do seguro.
Agora vamos desenvolver os métodos. Primeiro, precisamos inicializar os atributos da classe, e pra isso vamos usar um construtor.
Note que a classe mãe não possui um construtor. Isso quer dizer que a gente não precisa chamar o construtor da classe mãe. Então, vamos simplesmente fazer um construtor simples para a classe filha.
Outro detalhe é que o atributo é protegido. E isso quer dizer que a classe filha tem acesso direto a esse atributo, então podemos modifica-lo sem problemas.
Para desenvolver o construtor, é só você lembrar que construtores são funções sem tipo que têm o mesmo nome da classe:
O próximo passo é criar os métodos getters para que seja possível acessar os novos atributos privados:
Belezinha, a classe está quase pronta. Só falta desenvolver o método
Passo 3
Ainda dentro da nossa classe, vamos dar desenvolver o método .
A classe já têm um método chamado que foi herdado da classe mãe. Porém, a versão que foi herdada imprime apenas o atributo .
A solução é executar um polimorfismo! Nós vamos reescrever uma nova função com o nome .
Primeiro a gente declarou o método na linha 34, perceba que não é preciso escrever a palavra chave .
Depois, nas linhas 35 a 39 a gente escreveu novas instruções para o método !
Polimorfismo é super tranquilo de fazer. É só sobrescrever o método antigo com um novo método usando o mesmo nome. 😊
E pronto! A classe está concluída.
Passo 4
Show! Agora vamos testar pra ver se deu tudo certo.
Vamos criar um objeto da classe , e usando o construtor vamos preencher os valores dos atributos. Em seguida vamos executar o método :
E o resultado é esse:
Resposta
Exercício Resolvido #5
Adaptado de: Univasf – Prof. Leonardo Barreto Campos – Polimorfismo – Exercícios de C++
Observe a classe
Crie uma classe derivada de chamada que possui os atributos , , . Na classe crie um construtor para a classe e métodos getters para os atributos, e implemente o método que mostre os valores de todos os atributos.
Passo 1
Vamos que vamos, pessoal!
Essa questão já deu pra gente a classe mãe e pediu pra criar uma classe filha. O detalhe da questão é que ela pede pra gente criar um método dentro da classe filha: isso é um polimorfismo!
Vamos escrevendo a classe da questão dentro do nosso código:
Essa classe possui um atributo protegido e dois métodos públicos. O atributo que deve armazenar o nome do paciente, o método que retorna o valor do atributo e o método virtual que imprime um texto com os dados do paciente.
O método é virtual, isso quer dizer que desde o começo ele foi pensado para sofrer polimorfismo.
Passo 2
Vamos recordar como se cria uma herança:
Agora, vamos criar uma classe herdando a classe base :
Agora os atributos e métodos da classe mãe foram copiados para a classe filha. O próximo passo é adicionar os novos atributos que são exclusivos da classe filha:
Para inicializar os atributos da classe vamos desenvolver um construtor. Lembre-se que construtores são funções sem tipo que têm o mesmo nome da classe.
Fique atento a dois detalhes: o atributo é protegido e por isso podemos acessá-lo sem dificuldades, e o segundo é que a classe mãe não possui um construtor e por isso a gente não precisa chamar o construtor da classe mãe.
O próximo passo é criar os métodos getters para que seja possível acessar os novos atributos privados:
Belezinha, a classe está quase pronta. Só falta desenvolver o método
Passo 3
A classe herdou o método da classe mãe. Mas da forma como foi herdado, esse método não consegue utilizar todos os atributos da classe
A melhor solução é executar um polimorfismo que consiste em reescrever o método .
A vantagem de usar um polimorfismo é que a classe filha vai continuar com uma estrutura semelhante a da classe mãe. O que pode ser super útil quando a gente utiliza ponteiros, ou quando existem outros métodos que fazem chamada ao método “polimorfado”.
Para executar o polimorfismo, é só declarar o método novamente, e nem é preciso usar a palavra chave . Depois é só escrever o código que a gente quiser dentro do método “polimorfado”
Nesse caso, a gente vai adicionar vários para imprimir cada um dos atributos.
Só pra esclarecer “polimorfado” não é uma palavra de verdade 😂😂! Então não vai escrever isso na sua prova não 🤣🤣🤣
Passo 4
Show! Agora vamos testar pra ver se deu tudo certo.
Vamos criar um objeto da classe , e usando o construtor vamos preencher os valores dos atributos. Em seguida vamos executar o método :
E o resultado é esse:
Resposta
Exercício Resolvido #6
Adaptado de: Univasf – Prof. Leonardo Barreto Campos – Polimorfismo
Implemente uma classe abstrata de nome onde são declarados os métodos virtuais puros e .
Passo 1
E aí, galera.
Antes de mais nada, a gente precisa lembrar que um método virtual puro é um tipo de método especial que é vazio. A única utilidade de um método virtual puro é “guardar o lugar” dentro da classe mãe, para que uma classe filha efetue o polimorfismo desse método.
Para criar um método virtual puro é assim:
Usar um método virtual puro é principalmente uma decisão de design e é útil para garantir que todas as classes irmãs tenham certos métodos em comum. Isso é útil para manter a coesão do código.
Outro conceito para lembrar é que uma classe abstrata é uma classe que possui ao menos um método virtual puro. E se a classe abstrata possuir apenas métodos virtuais puros, então ela pode ser chamada de interface.
O que a questão está pedindo de verdade é que a gente construa uma interface que vai servir de base para outras classes no futuro. Como existem dois métodos virtuais puros dentro dessa interface, isso garante que todas as classes derivadas vão ser obrigadas a implementar versões próprias de como calcular uma área e de como calcular o perímetro.
Passo 2
Sem mais delongas, vamos escrever o cabeçalho do nosso código:
E em seguida, vamos criar uma classe chamada :
E determinamos o encapsulamento como público:
Agora vamos declarar o método como virtual puro, assim:
E depois, vamos declarar o método como virtual puro:
E aí podemos finalizar a classe!
Questão prontinha!
Resposta
Exercício Resolvido #7
Adaptado de: Univasf – Prof. Leonardo Barreto Campos – Polimorfismo
Observe a classe interface
Crie, uma classe de nome cujas instâncias são caracterizadas pelos atributos e ambos do tipo . Implemente na classe os métodos herdados e outros que ache necessários.
Passo 1
Lets go, galerinha!
A primeira coisa que você tem que notar é que todos os métodos de são virtuais puros. E isso é importante pois todos os métodos virtuais puros precisam sofrer polimorfismo. Para identificar um método virtual puro, olhe se ele tem a palavra chave e se termina com .
A classe que vamos criar é a que precisa de dois atributos e . O ideal é que esses atributos sejam privados, então vamos precisar também de um construtor para inicializar os atributos e de um par de getters para podermos visualizar os atributos.
Mas antes de tudo, vamos começar escrevendo a interface dentro do nosso código:
Passo 2
Belezinha, partimos agora pro desenvolvimento da classe filha, lembre-se que pra executar uma herança é assim:
Então criamos a classe que herda a classe
Os atributos e devem ser de acesso restrito, então vamos declarar os atributos com encapsulamento privado:
Em seguida, vamos criar um método construtor para iniciar os atributos. Os métodos construtores devem ser públicos.
Por questão de gosto pessoal, o nome dos parâmetros do método construtor vai ser igual ao dos atributos. Para evitar ambiguidades, vou usar o para indicar quais são as variáveis que representam os atributos da classe, e os que não tem essa indicação são os parâmetros inseridos pelo usuário.
Depois vamos criar métodos getters para os atributos:
O próximo passo é executar o polimorfismo
Passo 3
Ainda dentro da classe, vamos dar atenção aos detalhes da herança.
Foram herdados dois métodos virtuais puros. Esse tipo de método deve sempre sofrer polimorfismo!
Então vamos começar pelo método . Para aplicar o polimorfismo é só declarar um método utilizando este mesmo nome! O uso do não é necessário. Depois, dentro do método, é só a gente calcular a área do retângulo e retornar o resultado
E depois vamos efetuar o polimorfismo no método . Novamente, para executar um polimorfismo é só a gente escrever um novo método utilizando o mesmo nome .
E a classe está prontinha!
Passo 4
Agora é só a gente testar. Vamos criar um objeto da classe retângulo e vamos imprimir o resultado da área e do perímetro:
Resposta
Exercício Resolvido #8
Elaboração Própria
Os dois códigos abaixo implementam duas classes com herança e polimorfismo. A única diferença entre eles está na linha 07.
Qual o impacto dessa diferença? Escreva uma que demonstre o seu argumento.
Passo 1
Vamos lá, pessoal!
A única diferença entre os dois códigos é que na linha 07 o Código A utiliza a palavra chave e o código B não utiliza
Isso quer dizer que no código A o método é um método virtual, e no código B não.
Os métodos virtuais são uma forma de mudar o comportamento de um método que vai sofrer polimorfismo.
A mudança de comportamento acontece quando existe iterações entre a classe mãe e a classe filha de uma das duas formas:
- Quando um método que sofreu polimorfismo na classe filha é executando dentro de outro método da classe mãe.
- Quando usamos ponteiros da classe mãe para armazenar o endereço de um objeto da classe filha.
O informa ao C++ que ele sempre deve olhar pra classe filha em busca de polimorfismos. Caso o virtual não esteja presente, quando a gente executar um dos dois casos acima, a versão “polimorfada” será ignorada, e a versão da classe mãe será executada.
Você pode ver que o Caso 2 acontece nos códigos do enunciado. O método é executando dentro do método da classe mãe, e ao mesmo tempo sofre polimorfismo dentro da classe filha. Quando o não é utilizado, os métodos da classe mãe vão executar chamadas para métodos existentes na classe mãe.
De certa forma é como se quando a gente não usa o o polimorfismo só tem validade dentro da classe Filha, e métodos e ponteiro da classe mãe não vão levar o polimorfismo em conta.
Para ficar mais claro. Vamos executar os códigos A e B
Passo 2
Agora vamos ver um exemplo do código A em ação.
Vamos criar um objeto da classe filha e executar os métodos e
Quando o objeto executa o método ele sempre executa versão com polimorfismo, pois estamos fazendo uma chamada direta ao método.
Quando executamos o método estamos executando um método herdado. Esse método herdado vai executar o método , porém o método é virtual, e logo, a o sabe que deve conferir se o método sofreu polimorfismo.
Passo 3
Agora vamos ver como o código B se comporta.
Vamos criar um objeto da classe filha e executar os métodos e
Quando o objeto executa o método ele sempre executa versão com polimorfismo, pois estamos fazendo uma chamada direta ao método.
Quando executamos o método estamos executando um método herdado. Esse método herdado vai executar o método , porém o método é não virtual, e logo, a o simplesmente executa a versão existente na classe mãe.
Resposta
A ausência da palavra-chave modifica o comportamento dos métodos da classe mãe que fazem chamadas ao método