Se você olhar,
então, o polimorfismo propriamente dito,
a ligação dinâmica depende da herança.
A herança acaba sendo um elemento fundamental
para que o polimorfismo via funções virtuais ocorra.
A herança é tão rica
que ela permite, além de fazer uma classe herdar de outra,
além de um conjunto de classes herdar de uma classe,
além de um conjunto de classes
herdar de um classe com funções virtuais,
permitindo quadros de polimorfismo,
também a herança permite outras coisas,
como a herança múltipla, que é o caso aqui.
Com a gente entrando outro tópico de discussão
na nossa aula, que é a herança múltipla.
Uma classe pode herdar o conteúdo de mais de uma classe.
Vamos ver um exemplo prático disso.
Está aqui, eu criei uma classe Conhecimento,
inclusive tem um id comentado ali, deixa esse id aí.
Tem aqui um domínio.
Conhecimento tem um domínio, né?
Conhecimento de matemática,
Conhecimento relativo ao domínio da física.
Construtora, com parâmetro, com valor default,
que faz, por sua vez, uma estrutura sem parâmetros.
Estrutura, getDominio e setDominio.
Aqui eu tenho a classe livro,
que é derivado de Conhecimento.
Faz sentido, né?
Livro pode ser visto como um Conhecimento, por assim dizer.
Aqui eu tenho o Professor.
O Professor é derivado de Pessoa, com a gente já sabe,
mas Professor também é Conhecimento.
Se você tem uma dúvida de c++, por exemplo,
você pode consultar um Livro de c++ ou um Professor de c++.
Então, aqui eu estou fazendo Professor herda publicamente de Pessoa,
vírgula, publicamente de Conhecimento.
Aqui o Professor vai ser duas coisas,
vai ser Pessoa e Conhecimento.
Até isso é mais bem salientado no próximo slide.
Aqui eu usei um grafismo,
se fizesse uma mescla,
com o slide que foi proposto pelo monitor Vagner Vengue,
uma mescla de c++ e UML, por assim dizer,
é só um graceja gráfico, bem entendido.
Para deixar bem explícito,
Professor deriva de Pessoa
está ali, deriva de Pessoa,
e deriva de Conhecimento.
Inclusive, a Pessoa herda informaProventos, que é virtual.
De fato, vai herdar, também, de Conhecimento,
não afeta em nada, em relação a virtualidade que ele vai ter da Pessoa.
Ou seja, se no código de polimorfismo, que a gente viu antes,
mesmo o Professor herdando,também, de Conhecimento,
não afeta o código de polimorfismo em nada.
Não nesse código que está posto, em todo caso.
Agora, você tem que prestar atenção em algumas coisas.
Tome cuidado.
Porque aqui em Pessoa já tem um id,
então, se colocar outro id lá,
que id, no final das contas, ele vai estar utilizando?
Por isso o id aqui está comentado.
Tome muito cuidado.
Depois a gente vai comentar um pouco a questão do id,
mas, por enquanto, ele está comentado.
Aqui é interessante pessoal,
porque eu vou poder criar objetos de Professor, por exemplo,
que vai utilizar tanto métodos que ele tem em si,
por exemplo, setUnivFiliado ou informaProventos que tem aqui,
quanto utilizar funções das classes bases,
Pessoa com o calcIdade, está aqui nas reticências,
quanto de Conhecimento, por exemplo, setDominio e getDominio.
Vamos ver um exemplo aqui.
Está aqui, uma função na classe Principal inicializa Professores, que já é conhecido.
Veja que interessante,
Inicializa, aqui no Simão,
é uma função herdada de Pessoa.
setBolsaProjeto, já é uma função própria de Professor,
setSalario idem, setUnivFiliado idem,
já é uma função própria da classe Professor.
Inicializa, insisto, advêm da classe base Pessoa.
Já o setDominio, advêm da classe base Conhecimento.
Eu posso ter uma nova função,
Conhecimento por Professores, Conhecimento de cada Professor.
E está lá, por exemplo, Simao.getDominio,
Einstein.getDominio e Newton.getDominio.
Bem entendido, que tal qual
eu inicializei Simão e também inicializei Einstein e Newton,
Por isso eu posso chamar o getDominio de cada um.
Do Simão é "Computação",
do Einstein é "Física",
do Newton é "Matemática-Física",
e assim vai.
Então para vocês verem que é possível herança múltipla.
Falando em herança múltipla ainda, temos o seguinte:
Caso múltiplas superclasses tenham algum método com a mesma assinatura
e deseje-se utilizar uma destas funções, basta identificar o método com o nome da superclasse.
E aqui entra um exemplo.
Esse aqui também é um código feito pelo Vagner Vengue,
que é um bom código, um código interessante.
Eu tenho aqui Conhecimento, e agora resolvi colocar id de Conhecimento,
descomentei aquele id lá, e peguei o resto do código que já conhecemos.
Professor deriva de Pessoa e de Conhecimento.
E agora eu tenho aqui, por exemplo, uma construtora nova que eu criei
que recebe o RG e o IdGeral.
Onde eu vou colocar cada id?
Porque se você olhar, o Professor vai ter dois id,
vai ter o id de Pessoa e vai ter o id de Conhecimento.
Então, para armazenar o RG no id de Pessoa
eu uso essa estratégia: setId na parte Pessoa.
Agora eu quero o IdGeral, mas quero deixar no id de Conhecimento,
então, eu faço essa chamada aqui.
Geralmente, esse tipo de código funciona,
esse tipo de código eu fazia, muito tempo atrás, ele funcionava,
deve continuar funcionando nos computadores modernos.
Eu fazia esse tipo de códigos em computadores antigos,
com essa definição de escopo que funcionava muito bem.
Só que vocês tem q tomar muito cuidado com esse tipo de código,
para não confundir, porque vocês tem dois id,
no mesmo Professor,
por causa da herança múltipla dele.
Isso pode gerar uma série de confusões.
Quando vocês fizerem, por exemplo, Simao.getId,
ta pegando o getId de qual? De Pessoa ou de Conhecimento?
Então, muito cuidado com esse tipo de código,
esse tipo de código tem que ser evitado.
Inclusive, imagine o seguinte:
imagine que Pessoa e Conhecimento
fossem derivados de uma mesma classe,
uma classe X qualquer,
você iria fazer com que Professor
ia ser X por parte de Pessoa e ia ser X, também, por parte de Conhecimento.
O que cria o chamado losango ou diamante da morte,
e isso aqui é um pepino.
Então, se tem Professor, que é derivado de Pessoa e de Conhecimento,
Pessoa derivado da classe X e Conhecimento derivado da classe X,
ou seja, Pessoa e Conhecimento são derivados de uma mesma classe,
agora você tem Professor que é derivado de Pessoa, e de X portanto,
e de Conhecimento, e de X, de volta, portanto.
Cria a situação de alta instabilidade
chamado losango ou diamante da morte.
Esse é um assunto que eu gostaria que vocês estudassem,
nos livros de c++, enfim, nos fóruns de c++,
para a gente debater isso nos nossos...
encontros síncronos
ou tira dúvidas.
Isso aqui é um tema importante
de ser discutido e aprofundado.
Então, isso aqui é uma das possibilidades que a gente tem com herança.
Herança permite a gente fazer polimorfismo,
herança permite a gente herdar, naturalmente,
e permite fazer herança múltipla,
herança múltipla tem que fazer com muita cautela,
podendo fazer besteira,
particularmente, quando você cai no quadro do losango da morte.
O que ta aqui não é o caso losango da morte.
Ele seria se Pessoa e Conhecimento fossem derivadas da mesma classe,
classe hipotética X.
Outro assunto, também importante,
é a função virtual pura.
Que também se dá no quadro de herança.
Se você olhar aqui, eu estou retomando um exemplo
criado pelo Vagner Vengue e eu dei umas melhoradas no exemplo.
Aqui, novamente, eu volto com os exemplos com os nomes em latim,
para não misturar com nosso sistema acadêmico.
Depois a gente volta com o sistema acadêmico, se for o caso.
Aqui eu tenho, então, a classe Persona.
Eu vejo agora que agora eu tenho, na classe Persona,
além dos conteúdos que a gente já tinha,
eu transformei a função, que antes a gente tinha implementação,
não tem mais, ela recebe zero.
Uma função virtual que recebe zero, é uma função sem implementação.
Se eu tenho uma classe, por exemplo, Alumnus,
derivado de Persona, e Persona tem uma função virtual pura,
eu sou obrigado a redefinir a função.
Se eu não redefinir dá erro de compilação.
Mais uma classe, a classe Professor,
derivado de Persona.
Professor de Persona, também, tem que redefinir a função identificare,
porque na classe base Persona ela é pura,
então você obriga a redefinir na classe derivada.
Olha a execução do código aqui.
Eu tenho dois ponteiros, "pes1" e "pes2",
ponteiros de Persona, cada qual.
Vejam, que isso aqui são ponteiros,
eu posso criar ponteiros Persona, mas não posso criar objetos.
Se eu tentar criar um objeto "pes" de Persona, dá erro.
Dá erro porque não posso criar objeto de classe que tem função virtual pura.
Classe que tem função virtual pura, só serve para uma coisa, para derivação.
Aqui eu o "alu" que é Alumnus, e, portanto, também Persona,
e tem o "pro" que é Professor, e também Persona.
Então vai funcionar,
tanto o "alu" com o próprio identificare,
quanto o "pro" com o identificare definido em sua própria classe.
Aqui eu posso converter o "alu" para Persona,
e posso converter o "pro" para Persona, também.
Apontá-los como Persona, com o "pes1" e "pes2";
O que acontece se eu chamar o identificare?
Ora, como é virtual,
e naturalmente não tem implementação aqui,
ele chama a especializada, no caso ele vai imprimir "Alumnus".
No caso do Professor, através do virtual da ligação dinâmica,
ele vai imprimir o "Professor".
Vamos ler o que está escrito no slide para a gente registrar essa informação.
Um método ou função virtual pura não é implementável na classe em questão
e deve obrigatoriamente ser implementada nas classes derivadas!
No código de exemplo ao lado,
foi criada uma função virtual pura na classe Persona
e depois implementada nas classes derivadas.
O modo de uso destas funções virtuais puras
é igual ao da função virtual normal.
Entretanto, pode-se utilizá-las para criar classes
sem a necessidade de implementar todos os métodos,
apenas as suas interfaces,
podendo-se criar padrões para projetos,
obrigando todas as classes derivadas a redefinirem os métodos desejados.
Esse entretanto aqui justamente para apontar o que?
Apontar a vantagem.
Qual a vantagem de se ter uma função virtual pura,
já que ela vai agir, praticamente, igual a uma função virtual normal?
É o que está escrito ai.
Que usando funções virtuais puras
eu posso criar classes como Persona
que elas não precisam ter o identificare
e que só vão deixar isso a cargo das funções derivadas.
Se não faz muito sentido eu ter,
numa nova classe, a definição de uma função,
eu posso deixar ela ali.
Nas classes derivadas, isso precisa ser implementado.
Na verdade, isso nos trás o conceito de classe abstrata.
Está aqui nosso próximo exemplo, próximo slide,
que trás o exemplo da classe Conhecimento que a gente já tinha visto.
Uma classe abstrata não pode ser instanciada,
isto é, não se cria objetos a partir dela, mas pode ser derivada.
Define-se uma classe abstrata por meio de
pelo menos uma função virtual pura!
Uma função virtual pura não é implementada na classe em questão,
e deve ser, obrigatoriamente, implementada nas classes derivadas.
Então, a função virtual pura é a base do sistema classe abstrata.
Se tem uma função virtual pura na classe dada,
que aui, por exemplo, é o Conhecimento,
ela já vai ser abstrata por definição.
E é engraçado, porque a classe Conhecimento falta algo abstrato,
o Conhecimento é algo abstrato.
Ele toma forma quando você diz:
Olha, está aqui um livro de c++, tem Conhecimento de c++.
Está aqui um Professor de c++, parece ter um Conhecimento de c++.
Veja que, de fato, hora que eu recebo a função virtual que recebe zero,
ele não tem implementação no cpp.
Ela simplesmente não tem implementação, portanto não pode ter instância,
ela serve apenas como base de derivação.
Temos as derivadas, que, no caso de Livro, deriva de Conhecimento.
Inclusive, qual é a função pura aqui?
É a informaAntiguidade.
Do ponto de vista da classe Conhecimento isso não faz muito sentido,
mas a gente sabe que Conhecimento pode ter uma antiguidade.
Conhecimento de geometria tem uma antiguidade,
Conhecimento de física tem uma antiguidade,
começou em um dado momento do pensamento humano.
Aqui, por exemplo, um Livro derivado da classe Conhecimento.
Se na classe Conhecimento a função virtual,
existe a função virtual pura informaAntiguidade,
tem que ser reimplementada na classe Livro, está aqui.
Qual é a antiguidade do Conhecimento desse Livro?
O ano de impressão, por exemplo.
No caso do Professor eu sou obrigado a fazer o informaAntiguidade.
Qual é a antiguidade desse Conhecimento?
A idade do Professor? Talvez a idade menos 20,
menos 25,
concluindo que a gente vai se formar com mais ou menos 25 anos.
Então, eu posso ter um informaAntiguidade baseado na idade menos um valor.
Aqui eu deixe a Idade mesmo, mas podiam colocar um valor qualquer.
Ah não professor, aqui tem que ser a data que ele foi...
que ele entrou como professor na universidade.
Eu tenho um atributo anoDeIngresso,
e colocaria o informaAntiguidade como anoDeIngresso.
O Professor com Conhecimento em c++.
O fato é que tem que reimplementar nas classes derivadas,
dando sentido para essa abstração.
Faz sentido que o informaAntiguidade de um Livro seja o ano de impressão.
Faz sentido que o informaAntiguidade de um Professdor seja algo como
a idade menos 25, ou o ano de ingresso dele como Professor, ou algo assim.
Bom pessoal,
o fato de se usar uma função virtual pura
não atrapalha o polimorfismo.
Deixa eu voltar alguns slides.
Se aqui, por exemplo, nesse quadro ou no quadro anterior
o informaProventos fosse virtual pura,
o que iria acontecer?
Eu iria ser obrigado a redefinir a função informaProventos no Aluno.
Se aqui fosse virtual pura,
eu sou obrigado a definir em cada uma das derivadas.
Coisa que eu teria feito, como fiz aqui.
Então, se aqui fosse virtual pura
eu seria obrigado a fazer isso que está em vermelho.
Deixaria virtual ainda sim.
Então, a função virtual normal pode implementar uma função virtual pura.
E isso iria permitir fazer o quadro de polimorfismo,
algo similar ao que acontece aqui.
Então, o fato de eu fazer função virtual pura em uma classe base, como Pessoa,
que não é o caso, mas se fosse
isso não afetaria execuções polimórficas.
Única coisa é que em qualquer classe derivada
seria obrigado a redefinir a função informaProventos.
Bom pessoal,
a gente fez um belo estudo sobre elementos que envolvem herança.
Particularmente, um destaque para as funções virtuais,
incluindo virtuais puras.
Tanto as funções virtuais normais quanto as funções virtuais puras
elas permitem o quadro de polimorfismo propriamente dito.
Foi isso que a gente discutiu no âmago, no principal dessa aula de hoje.
Dito isso, eu deixo exercícios.
Exercício A.
Aplicar no código desenvolvido neste presente grupo de slides,
a solução apresentada no grupo de slides 10 - parte B.
Basicamente o seguinte:
invés de eu ter, por exemplo, uma lPessoas,
lista parametrizada com Pessoa,
eu vou usar uma solução mais desacoplante,
que é a solução proposta no grupo de slides 10 - parte B.
Ainda tem a outra parte desse exercício proposto.
Que é o item B, que é o seguinte:
Localizar em livros ou páginas ou afins sobre c++ o chamado "losango da morte"
ou "diamante da morte", reimplementando o exemplo lá disponível.
Vocês vão buscar na literatura de c++ ou nas páginas ou afins
um exemplo de losango da morte e vão implementá-lo
e entender as peculiaridades disso,
e saber que via de regra você não deve fazer o losango ou diamante da morte,
e se você quiser você tem que fazer com toda precaução que vai estar indicada na literatura.
Por fim, tem alguns itens aqui para você revisar ou estudar.
O que está em verde para você estudar,
o que está azul...
perdão, o que está em verde para revisar,
o que está em azul para você estudar.
Boa parte desse conteúdo está proposto em grupos de slide para frente.
Proponho você revisar sobrecarga de métodos
ou de funções membros ou de operações.
Métodos também são chamados de funções membro ou de operações.
Estudem sobrecarga de métodos,
o que aqui a literatura as vezes chama, bizarramente,
de polimorfismo de métodos ou polimorfismo de função.
É o nome que digo bizarramente porque pode confundir com
o polimorfismo propriamente dito, com a comunicação dinâmica.
E sobrecarga de métodos, na verdade,
muito embora você tenha funções com implementações distintas
não tem nada a ver com ligação dinâmica,
como a gente viu nas explicações feitas nessa aula.
Polimorfismo propriamente dito
é baseado em ligação dinâmica
e em tabelas de virtualidade, no fim das contas.
Agora sim, avançando para sobrecarga de operadores,
foi coisa que já foi discutida nos slides anteriores.
Não confundir sobrecarga de métodos com sobrecarga de operadores, por sua vez.
Como os métodos as vezes são chamados de operações,
as pessoas as vezes confundem sobrecarga de operações com sobrecarga de operadores.
São coisas distintas,
operador é um coisa e operações ou métodos são outras.
Isso da parte de constância.
A parte de classe pré-definida String.
Os tipos de cast, além do static cast vai ter o dynamic cast, reinterpret cast.
Espaço de nomes, que a gente vai ver mais para frente.
Classes Aninhadas, idem.
Atributos e métodos estáticos, também idem.
Já podem ir vendo esses tópicos ai para frente,
particularmente esses que estão em azul, esses últimos três,
para adiantar assunto.
Quando você ver minha exposição, você já estudou os slides,
já está com a coisa mais conformada, mais bem absorvida do conteúdo em questão.
Pessoal, essa foi a aula de hoje, a exposição de hoje.
Espero que tenham gostado.
A gente se vê na próxima exposição.
Bons estudos, até breve.