Isso considerado, a gente armou agora um cenário aqui,
para a gente poder entender, ou aplicar no caso,
os chamados Métodos ou funções virtuais.
O que que é uma função virtual?
Aqui eu trago um exemplo para explicar
Um exemplo outro, não o do sistema acadêmico.
Um exemplo suplementar, criado pelo...
então monitor da disciplina, Vagner Vengue.
Não era bem esse o código que ele criou, eu adaptei,
mas a ideia geral era essa.
Esse exemplo vai permitir a gente entender
o que é função virtual ou método virtual,
e aplicar no sistema acadêmico.
Qualquer método pode ser redefinido em uma classe derivada.
O que for elemento normal.
Por exemplo, o método getdata()
definida na classe Persona, ao lado,
pode ser redefinido na classe derivada Alumnus.
Aqui eu criei uma classe com os nomes em latim,
para não confundir com as classes do sistema acadêmico.
Então, a classe Persona, por exemplo, tem um string nomen,
nomen é inicializado na estrutura com "Humanun".
Aqui eu tenho um getdata que imprime só o nome.
Obviamente, que aqui tem errinho de digitação.
Vai imprimir o nomen ali no getdata.
Eu também tenho a função void chamada identificare, e diz "Persona!".
Ou seja, que é conteúdo de uma pessoa.
Eu disse que eu peguei a função getdata,
e eu posso redefini-la na classe Alumnus.
De fato, a Alumnus é derivada de Persona,
que tem um atributo inteiro que é code.
Ou, no caso, como está em latim deve ser "códe", suponho, a pronúncia.
Aqui eu não coloquei private, então private por definição.
Não da erro isso, ele sempre vai ser privado por definição.
E aqui eu redefini o getdata.
Inclusive, esse getdata, obviamente ele vai ter que imprimir nomen, não nome.
E naturalmente que esse código aqui vai precisar,
para que ele tenha acesso ali.
Desse jeito o código não funcionaria.
Eu to querendo imprimir um nomem com o getdata,
mas vejam que a classe Alumnus derivou o Persona,
para acesso direto o que é público e protegido, e não tem nada protegido aqui.
As coisas são privadas.
Eu não consigo ter um acesso direto a nomen aqui portanto.
E aqui eu não tenho um getnomen para ter acesso.
Então, aqui, ou eu crio um getnomen e chamo o getnomen aqui,
ou transformo esse atributo ali em protected.
Que daí passa a funcionar.
Agora sim, está correto.
E agora isso volta a funcionar.
O getdata vai imprimir nomen herdado da classe base,
tem nomen como protegido.
Então ele tem, naturalmente, acesso direto a classe derivada.
E o code que já dele e tem acesso direto.
Mesmo sendo privado, faz parte dele e vai ter acesso direto.
Veja que foi redefinido o getdata, lá e aqui.
Muito bem.
Se eu criar um objeto de Alumnus
e fizer "a" do tipo Alumnus, por exemplo e fizer "a" ponto getdata,
ele vai imprimir isso aqui.
Vai imprimir nomen e vai imprimir, também, code.
Ele vai imprimir "Humanun" e "12", que é valor como foi inicializado aqui.
Agora, se eu apontar o Alumnus enquanto Persona,
se eu tiver um ponteiro que aponta Alumnus no Persona,
a coisa muda de figura.
É o caso aqui, eu tenho um "a" do tipo Alumnus,
eu tenho um ponteiro do tipo Persona.
Eu to convertendo, ou eu to enxergando o endereço,
de Alumnus, só enquanto Persona.
Se eu fizer, agora, um getdata de um cara apontado a partir de "p",
quem que é apontado a partir de "p"?
É o "a", só que eu estou enxergando apenas a parte Persona do Alumnus.
Então ele vai acabar chamando o getdata lá de cima.
Estou enxergando a parte Persona, ele não está enxergando a parte Alumnus.
O ponteiro "p" aponta para o Alumnus, que também é uma Persona,
mas o "p" sendo um ponteiro de pessoa,
embora ele aponte para Alumnus, ele só enxerga a parte Persona.
E é por isso que, se eu fizer agora um getdata a partir do ponteiro "p",
ele não vai conseguir enxergar esse getdata aqui.
Ele enxerga o getdata mais primitivo.
Com isso enxerga a parte Persona.
Volta agora aqui então.
Qualquer método pode ser redefinido em uma classe derivada.
Por exemplo, o método getdata, definido na classe Persona ao lado,
pode ser redefinido na classe derivada Alumnus.
Assim, um conjunto de Alumnus,
aliás, perdão, assim o objeto Alumnus, como o objeto "a" ali,
usaria o método getdata definido na sua classe.
Isso é bem óbvio, conforme a gente já discutiu.
Assim, um objeto de Alumnus usaria o método getdata definido em sua classe.
Entretanto, se ele fosse convertido, cast, para Persona,
ou apontado como Persona, que é o caso da função main,
nesta dada conversão,
utilizaria o método getdata definido na sua superclasse.
Ou seja, na classe base, a classe Persona no caso.
Que foi o que eu expliquei pra vocês.
Aqui, para ser perfeccionista, vou colocar uma vírgula.
E tem uma parte vermelha aqui.
Entretanto, se está em vermelho tem que ser destacado.
Entretanto, se o método for virtual,
como no caso de identificare, ta aqui virtual,
mesmo se apontado por ponteiro de classe base,
continua a ser executado o código da classe derivada.
O que é algo extraordinário, gente.
Por exemplo, no código ao lado, na função main,
primeiramente foi criado um objeto da classe Alumnus,
classe essa, desse objeto, derivada da classe Persona.
Aqui, objeto "a" do tipo Alumnus.
Objeto "a" da classe Alumnus, e essa classe derivada de Persona.
E um ponteiro de objeto da classe base Persona.
Está aqui, ponteiro de Persona.
Persona também chamada de superclasse ou classe base.
Mas não é para apontar para ponteiro de Persona apenas?
Mas Alumnus é uma Persona, além de Alumnus.
Então isso aqui funciona.
Então o "p" vai conseguir apontar para o "a",
enxergando só a parte "a", supostamente.
Tanto é que, na hora que faço "p" getdata,
ele vai imprimir "Humanum".
Por quê? Porque o "p" enxerga a parte Persona,
e não a parte Alumnus do Alumnus.
Ele imprime aqui "Humanun", e não "Humanun" e "12".
Mas veja que interessante,
o identificare não é igual o getdata,
só um void, ele é virtual void.
O que nos leva a essa parte do texto aqui.
Subsequentemente, foi então feita a conversão
de apontamento do endereço do objeto de Alumnus
para um ponteiro Persona, de fato.
Deste modo, dado que identificare é método virtual,
o programa deve imprimir no console o seguinte como resultado:
"Persona et alumnus",
Vejam só o que aconteceu gente,
isso aqui é um momento muito importante da aula,
provavelmente o momento mais importante da aula.
"p" está apontando para a parte Persona de Alumnus,
não para a parte Alumnus inclusive, apenas para a parte Persona.
Por isso, o getdata apresenta "Humanum".
Por que agora eu venho e chamo o identificare a partir de "p",
e ele está imprimindo a parte "Persona et alumnus"
da parte Alumnus, e não da parte Persona?
Porque tem a palavra virtual na frente.
Se tem a palavra virtual na frente,
mesmo que você esteja apontando para um objeto,
enquanto seu elemento mais primitivo,
ele consegue fazer um jump e acessar a parte mais especializada,
por causa dessa marca virtual, que leva a enxergar a parte mais especializada.
Então insisto, muito embora esteja enxergando, via "p",
o "a" só como Persona,
o Alumnus "a" só como Persona,
tanto é que o resultado de getdata é "Humanun",
não "Humanun" e "12",
quando eu chamo o identificare, como tem o virtual ele fala:
"Opa! Tem virtual aqui."
Então isso significa que essa função,
possivelmente, é de uma parte que esse apontamento não está enxergando.
Mas o virtual permite que o apontamento enxergue
só a parte identificare especializada, e imprime a parte especializada.
É esse o resultado.
Se a função idenficare não fosse virtual,
ele não conseguir chegar em "Persona et alumnus",
essa visão especializada.
Mesmo sendo apontado por Persona,
iria imprimir só "Persona".
Tal qual acontece com o getdata, que não é virtual.
Essa é a explicação geral.
Agora, para o que isso serve?
Serve para aumentar o nível de coesão e desacoplamento das coisas.
Vamos aplicar isso no exercício 1.
Está aqui a Pessoa.
Agora a Pessoa, da classe Pessoa que é a classe base
de Professor, classe base de Aluno,
que é classe base também, inevitavelmente, de Estagiário,
que é Aluno, e Pessoa é classe base de Aluno.
Então, na prática, Pessoa, em alguma medida,
é classe base de Professor, de Aluno e de Estagiário.
Estagiário indiretamente por meio de Aluno.
Se você olhar aqui agora,
o Estagiário já tinha um informaProventos,
Lá em Alunos não tem informaProventos, mas tudo bem.
Em Professor tem informaProventos.
Agora tem informaProventos, também, em Pessoa, que é virtual.
O que faz a implementação de informaProventos na Pessoa?
Ele está em implementação aqui,
"Nenhuma informação sobre proventos de Fulano de Tal."
Nome dele ali.
Novamente, qualquer método pode ser redefinido em uma classe derivada.
Por exemplo, o método informaIdade
poderia ser redefinido na classe Professor.
Assim o objeto Professor usaria o método,
ou função membro informaIdade definida na sua classe.
Naturalmente, pois a função é especializada.
Entretanto, se o Professor,
um professor qualquer,
fosse convertido, forjado, cast,
para Pessoa, ou apontado por Pessoa,
ele utilizaria o método informaIdade definido na sua superclasse, a classe Pessoa.
Já no caso da função ou método virtual,
mesmo que o objeto seja apontado pela sua superclasse,
ele continuará utilizando o método de sua classe.
Se eu tiver um Professor que for apontado por Pessoa,
um ponteiro "p" qualquer,
e eu fizer "p" setinha infomaProventos,
ainda assim ele vai conseguir enxergar a parte Professor.
Conforme a gente vai verificar com os nossos próximos slides.
Aqui para vocês terem um vislumbre da coisa.
Então eu tenho Pessoa que serve de base para Professor,
Professor derivado de Pessoa.
Eu tenho Pessoa que serve de base para Aluno,
Aluno derivado de Pessoa.
Eu tenho Estagiário que é derivado de Aluno, Aluno, por sua vez, derivado de Pessoa.
Professor tem informaProventos.
Estagiário tem informaProventos com funções mais especializadas.
Aqui ali ele faz um cálculo, aqui ele informa o valor.
Enquanto Aluno não tem informaProventos nenhum. Pelo menos não por ora.
E tem o informaProvento em Pessoa, que vocês já viram.
Que diz só o seguinte:
"Nenhuma informação sobre proventos de Fulano de Tal."
É isso que faz essa implementação, implementação virtual inclusive.
Aqui eu lembro, novamente nesse slide,
que Pessoa tem informaProventos no virtual.
Tornar o método informaProventos virtual
possibilitará às classes derivadas redefinir o método
e manter essa redefinição mesmo que o objeto seja "convertido"
ou apontado para a classe base, Pessoa por meio de ponteiros apropriados.
Que é o que vocês vão ver no próximo slide.
Está aqui a classe principal, uma função chamada ListeProventosPessoas.
E faz o que?
Vai navegar na lista de Pessoas,
recuperando cada elemento,
e a partir de cada elemento, recuperando cada Pessoa,
para chamar o informaProventos das Pessoas.
Bem entendido,
aqui eu utilizei aquela abordagem
de criar uma lista direto na classe Principal,
que é uma lista parametrizada com Pessoa,
que é uma lista com algum nível de acoplamento.
Tanto é que eu tenho, dentro da própria Principal, o algoritimo de listagem.
Depois quando vocês forem fazer uma versão mais desacoplada,
essa função listagem vai estar dentro de uma classe listaPessoas, não dentro da Principal.
O que interessa para nós nesse momento é o seguinte:
é entender como a função virtual definida na classe Pessoa
vai agir no âmbito dessa listagem.
Criei um ponteiroElementoPessoa para poder navegar nos elementos da lista.
Daí tem um elemento Pessoa
para cada vez que eu localizar um elemento
eu recuperar a Pessoa tratada por esse elemento.
O ponteiroElementoPessoa aponta para o primeiro elemento,
Olha lá, o LPessoas.getPrimeiro().
Enquanto nos elementos,
o elemento que eu tiver navegando não for nulo,
sendo que eu vou sempre navegar de próximo,
do inicio para o fim e tal.
Eu faço o que?
Cada vez que eu acesso um elemento,
eu recupero a pessoa tratada por esse elemento,
atraves do getInfo.
Agora que eu tenho a Pessoa,
eu venho aqui e chamo o informaProventos. para Pessoa.
Professor, como você está enxergando só Pessoas,
mesmo que ele seja Professor,
mesmo que ele seja Aluno,
mesmo que ele seja a Pessoa em questão Estagiário?
Eu estou enxergando apenas a parte Pessoa desses caras.
Porque você incluiu os endereçamentos na lista de Pessoa,
numa visão, num apontamento de Pessoa.
Então ele não teria acesso à parte especializada.
Então o que iria acontecer
é que ele iria imprimir para todo mundo
"Nenhuma informação sobre proventos de Fulano de Tal."
Por que isso?
Insisto, porque ele estaria enxergando apenas a parte Pessoa.
Só que tem um detalhe,
fundamental a função virtual.
Quando a função é virtual,
ele faz uma ligação dinâmica.
Ele consegue, através das chamadas tabelas de virtualidade,
através de um artifício,
que permite, mesmo que ele esteja a entrada como Pessoa,
enxerga a função informaProventos mais especializada, se houver.
Então, se houver um Professor, ele vai chamar a especializada de Professor,
através dessa ligação dinâmica.
Se tiver de Aluno, ele vai chamar de Aluno, mas não tem.
Se tiver em Estagiário, ele vai chamar de Estagiário.
Ele consegue descer, até a classe mais especializada
para verificar se ela tem função especializada
para ser impressa, ou não, através da ligação dinâmica.
Então o resultado é esse aqui.
Então, está aqui a função listarProventos,
função essa que percorre a lista aqui na unha
recuperando cada pessoa que está na lista a partir de cada elemento
e chamando o informaProventos de cada Pessoa.
Como em Pessoa a função é virtual, ele consegue chamar
a mesma ponteiroPessoa a função mais especializada do objeto apontado.
Olha aqui, os primeiros elementos, as primeiras Pessoa que tem na
lista de Pessoa são os Alunos, enxergados como Pessoa.
Como a classe Aluno
não redefine a função informaProventos,
a virtualidade não tem para onde apontar
e acaba utilizando a função de base da classe Pessoa.
Já os elementos subsequentes que tem na lista de Pessoas são professores.
Como Professores tem função informaProventos especializada,
a virtualidade permite chegar na parte especializada,
mesmo que no primeiro momento o elemento esteja sendo enxergado como Pessoa.
Insisto, a virtualidade cria a ligação dinâmica, que chega até a parte do Professor.
Na sequência ainda, temos dois Estagiários que são colocados como Pessoa.
A virtualidade permite chegar lá na função especializada feita no Estagiário,
através da ligação dinâmica e chamar a especializada de Estagiário.
Interessantíssimo, pois acontece o que aqui?
Eu consigo listar um conjunto de Pessoa,
de proventos de Pessoas,
que é esse item 7 que eu criei agora.
E aqui o interessante o seguinte,
Eu estou listando Pessoas, que, supostamente, deviam ser iguais,
mas eles tem, na função informaProventos,
formas distintas de informar as coisas.
Daí, tendo um comportamento polimórfico de diversas formas,
conforme seu subtipo.
Base para o polimorfismo.
Métodos, ou funções-membros, polimórficas.
Aqui eu vou deixar polimóficos, pois está concordando com métodos.
Métodos, ou funções-membros, polimórficos:
Dois métodos em classes diferentes com mesma assinatura, mesma forma,
mas com implementações diferentes,
sendo estas classes derivadas de uma mesma classe base
e estes métodos reimplementações de um método virtual definido nesta classe base.
Que é basicamente o que vocês estão vendo aqui,
nesse quadro que eu já expliquei para vocês.
O fato de eu ter o virtual aqui,
hora que eu faço Professor
ser apontado enquanto pessoa,
o fato do virtual, cria uma conexão
para ele continuar chamando o informaProventos,
através de um artifício de implementação chamado tabelas virtuais.
O que vocês precisam entender é que pelo fato de ser virtual,
mesmo que aponte Professor enquanto Pessoa,
ainda assim, a virtualidade permite chegar na parte informaProventos.
Mesma coisa aqui.
Se eu pego um Estagiário e aponto ele enquanto Pessoa,
ele vai, porque tem a palavra virtual aqui,
criar um apontamento para essa função especializada na classe Estagiário.
Que daí ocorre esse comportamento dado,
que é o polimorfismo ocorrendo.
São Pessoas, que supostamente seriam iguais,
mas que tem comportamento distinto.
Aqui tem uma outra consideração a ser feita.
Imagine que na classe Aluno eu tenho uma função informaProventos.
Se ela é a bolsa de permanência daquele Aluno,
imagine que aqule Aluno tem um valor de bolsa de permanência,
que pode ser zero inclusive,
mas conforme o caso não é zero,
ele iria ser informado aqui.
Se aqui tiver um informaProventos implementado,
tem que ser virtual também,
para não prejudicar o link com Estagiário.
Se não for virtual aqui,
hora que converto Estagiário para Pessoa,
ele vai linkar o provento dado aqui, e não lá embaixo.
Se eu quero que ele chegue lá embaixo, eu tenho que dizer
é virtual, continue virtualidade até chegar lá embaixo.
Então se tiver classe intermediária entre
a classe base base e a classe folha,
e tiver funções que são visadas no polimorfismo,
na minha classe intermediária eu tenho que colocar essa função como virtual também.
Isso aqui é interessantíssimo,
justamente porque permite esse comportamento polimórfico que vocês estão vendo, que vou colocar o slide.
O slide 27, o slide 28 é apenas uma consideração
se tivesse uma função informaProventos no Aluno,
ela teria que ser virtual pelo motivo já explicado.
Aqui voltando no 27,
aonde Aluno não tem informaProventos,
tem esse resultado que foi dado, que é um resultado polimórfico.
Para convencer melhor vocês da importância disso,
o quanto isso é desacoplante,
imagine a seguinte situação,
que você tenha um jogo, você vai fazer um jogo orientado a objetos,
você tem uma classe base chama Personagem,
que se deriva em Jogador e Inimigo.
Em Inimigo você tem derivado outros caras,
por exemplo, você tem derivado de Inimigo o Boitata,
você tem a CucaMaluca, você tem o Corrupto,
você tem a MulaSemCabeca, e assim você vai.
Se você parar para pensar, é uma hierarquia aqui.
Insisto, você tem a classe Personagem,
que se deriva em duas, Jogador e Inimigo,
e Inimigo por sua vez vai se derivar nessa que eu falei,
Boitata, MulaSemCabeca, Corrupto, e por aí vai.
Imaginem que cada uma dessas classes derivadas,
ou dessa árvore de derivação, tem uma função movimentar.
Vocês sabem que o movimentar do Boitata
vai ser diferente do movimentar, graficamente falando,
da MulaSemCabeca, e do movimentar do Corrupto,
e do movimentar da CucaMaluca.
Mas, se você parar para pensar,
quando eu estiver no Principal do meu jogo,
eu vou precisar que todos os Personagens se movimentem.
Eu gostaria que eles simplesmente se movimentassem,
eu gostaria de percorrer esses Personagens, e olhe, Personagens, movimentem-se.
Sem se preocupar se eles vão se movimentar do mesmo jeito ou de jeitos diferentes.
Você na classe Personagem chega na função movimentar,
e essa função for virtual,
isso me permite criar objetos dos mais distintos tipos,
por exemplo, vários Boitatas,
vários CucaMalucas,
vários Corruptos,
vários sei lá mais o que.
Cada qual tem sua função movimentar,
mas todos vão estar registrados numa lista de Personagens,
lembrando, Personagem tem função virtual movimentar,
isso me permite percorrer uma única lista,
de todos Personagens existentes no meu jogo,
e mandar esses Personagens se movimentarem.
Por exemplos, está apontado enquanto Personagem,
como Personagem tem função virtual movimentar,
ele vai acabar chamando as funções mais especializadas do polimorfismo.
Fazendo cada qual seu movimento conforme seu tipo.
O Boitata voa,
a MulaSemCabeca cavalga,
a CucaMaluca anda arrastando a calda.
Então, eles tem maneiras diferentes de se movimentar.
Sobre o ponto de vista de criatividade, eu sei que eles se movimentam.
Então, eu mando, Personagens, movimentem-se.
E eu vou apontar para todos esses caras como se fossem Personagem,
mas na hora que eu vi o apontamento
eu chamar a função movimentar,
a função movimentar é virtual,
pela ligação dinâmica, ele vai chamar as funções especializadas,
que era para esse Personagem se movimentar,
dando uma diversidade de movimentos para os Personagens do jogo,
com uma única lista, que é a lista de Persongens.
Não brigando com uma lista específica para cada subtipo.
E essa é a importância do polimorfismo,
isso cria um grande desacoplamento,
porque eu posso enxergar todos esses Personagens,
aqui eu coloquei 4 inimigos,
mas a gente podia ter 400 tipos de inimigos,
cada qual com movimentação diferente.
Esses 400 seriam todos enxergados como Personagem,
que iriam movimentar.
Como ocorre ligação dinâmica,
eles iriam fazer movimentação conforme seu subtipo,
sem precisar se preocupar com isso
do ponto de vista da lista geral de Personagens,
criando um grande desacoplamento.
Isso é interessantíssimo.
E isso explica, ou exalta,
uma riqueza da orientação a objetos, que é, justamente, o polimorfismo.
Aqui já é o slide 28, já explicado.
Não confundir o polimorfismo com sobrecarga de métodos.
Sobrecarga de métodos é o que vocês estão vendo aqui,
com estrutura Pessoa com duas implementações,
com a função membro, o método Calc_Idade,
com duas implementações com parâmetros distintos.
Isso aqui é sobrecarga de métodos.
Uma parte da literatura vai chamar isso de polimorfismo de métodos,
que são métodos com duas formas diferentes.
Eu não gosto desse nome na literatura,
por isso uso sobrecarga de método.
Bom, ainda que parte da literatura chama de polimorfismo de método,
não confundam com polimorfismo propriamente dito,
que tem essa ligação dinâmica,
que é que mostrei nos slides anteriores.
Então, não confundam sobrecarga de métodos com polimorfismo,
que é aquela coisa rica que expliquei para vocês,
tanto nesse exemplo polimórfico do sistema acadêmico,
quanto aqueles exemplos hipotéticos
de um jogo orientado a objetos, graficamente falando.