Ok. Avancemos Agora.
Então tá aqui o nosso Elemento template,
é um Elemento agora genérico,
ele é parametrizado com algo.
Eu sempre vou tratar um pInfo que eu não sei o que é
porque depende do tipo
e o pProximo e o pAnterior sempre vai ser também de Elemento,
ponteiro de Elemento parametrizado com o tipo.
Uma vez que eu passo o tipo que vai pra execução,
ele se adapta aqui por assim dizer,
ou se encaixa, ele se enquadra no parâmetro dado.
Aqui eu tô criando a ListaAlunos
Eu tô no slide 10 desse Grupo,
slide 10 do Grupo em questão.
Classe ListaAlunos.
Aqui um conjunto de atributos já usuais
cont_alunos, numero_alunos e o nome da lista.
Se eu quiser pôr um nome na lista.
E aqui eu tenho um pElAlunoPrim e um pElAlunoAtual,
o que é natural, né.
Eu sempre tenho o pPrimeiro e o pAtual.
Bem entendido que agora eles são ponteiros de Elemento parametrizado com Aluno.
Vou ter construtora, vou ter destrutora,
vou ter limpaLista, incluaAluno,
listeAlunos, graveAlunos e recupereAlunos.
O que muda nessa jogada toda aqui
é que o que antes era ElementoAluno
passa naturalmente ser Elemento parametrizado com Aluno.
Aqui na limpaLista.
É o que acontece, Elementos parametrizado com Aluno.
Se eu for lá no incluaAluno,
eu recebo lá o endereço de Aluno,
pra eu cuidar de um Aluno na lista eu tenho que ter um Elemento pra gerenciá-lo.
Daí eu crio aqui o pAux que é o ponteiro de Elemento parametrizado com Aluno.
Aqui eu vou criar o objeto efetivamente.
Vou criar o Elemento parametrizado com Aluno
que é apontado pelo ponteiro pertinente.
Através desse ponteiro,
eu consigo, por exemplo, no Elemento
replicar o nome do Aluno.
No Elemento parametrizado com Aluno que vai cuidar do Aluno em questão,
eu replico o nome dentro dele.
Aqui, eu faço o quê?
Faço, também, e isso é fundamental,
é o Elemento, né, em questão,
receber a referência
do objeto que vai ser tratado na lista,
que é o Aluno que tá vindo lá, ó.
Daí, faço aqui os testes condicionais de entrada.
Se ele passar, daí ele vai testar se é primeiro.
Se for primeiro faz as amarrações de primeiro e de atual,
senão faz as amarrações de atual, anterior
e atualização do atual.
e ainda faz aqui, depois, o incremento de contagem de qualquer forma.
Aqui, a gente pode fazer, inclusive, um pequeno ajuste
em tempo de execução, né,
porque a gente sempre tá ajustando as coisas, tá.
Então, nada obsta, da gente vir aqui,
abrir um pequeno espaço aqui
e deixar esse código aqui dentro,
pelo mesmo motivo que a gente já fez
em situações passadas, tá,
que só tem sentido esse código executar
se aqueles ifs forem aprovados.
Se os ifs não forem aprovados
ou se as condições do if acima, melhor especificando,
não forem aprovadas,
aí não faz sentido esse código, aí, em azul executar em nenhum momento.
Em sendo aprovada,
aí faz todo sentido o código azul
em questão, ser sim devidamente executado.
Aqui eu tô necessitando de espaço ali em baixo,
então, o que eu vou fazer é puxar isso aqui pra cima.
e tá resolvido.
Ajuste feito,
mais elegante, inclusive.
Aqui a gente tem uma situação de listagem.
O que a listagem faz?
Listagem ela vai navegar nos Elementos
e a partir dos Elementos imprimir o nome dos camaradas
que estão na lista.
Então, eu crio pra isso um auxiliar
que é um ponteiro de Elemento parametrizado com Aluno.
Aí, eu faço o paux apontar pro primeiro.
Enquanto o paux não for nulo, né,
eu faço o paux indo pro próximo.
paux recebe paux próximo.
Aí, o que que eu faço ali dentro?
O que eu faço ali dentro
é imprimir o nome de Aluno.
Aqui eu não tô dentro da Disciplina,
então eu consigo ter acesso direto ao nome.
E como é que eu tenho acesso ao nome do Aluno.
Então, aqui, se vocês olharem
eu repliquei o nome de Aluno dentro do Elemento.
Assim, se tá replicado o nome
ele vai imprimir o nome que foi replicado dentro de Elemento aqui.
Sem precisar ir fazer acesso direto ao Aluno.
Só usando o nome que tá replicado dentro do Elemento.
A questão que se impõe é se isso é bom ou se isso é ruim.
Bom, na minha opinião,
a resposta pra pergunta é que é ruim.
Inclusive, eu vou até destacar isso aqui.
Tá sendo destacando em vermelho aqui que é ruim.
É ruim porque replicar informação
neste tipo de sistema que a gente tá trabalhando aqui
não procede porque eu posso criar inconsistências.
Imagine que eu criei uma lista com, sei lá, 1000 alunos.
num dado contexto.
E aí, ficou replicando dentro dos elementos o nome dos alunos.
Depois eu vou atualizar o nome de algum Aluno lá, por exemplo,
de aluno fulano, aluno ciclano, aluno beltrano.
Daí eu vou ter que garantir
que os nomes replicados nos outros lugares
também fiquem sincronizados
eu vou ter que localizar cada elemento onde tem o nome replicado
pra poder fazer, lá, a atualização do que ocorreu no aluno ele mesmo.
Pra evitar esse tipo de situação
é o que eu faço a solução que segue.
Tá aí a solução.
Comentei essa linha aqui, tá.
Até poderia alterar a coisa na classe, coisa que eu não fiz ainda,
mas eu comentei essa linha aqui.
O nome lá dentro do Elemento vai ficar vazio.
mas daí como é que eu vou conseguir imprimir
o nome dos caras, professor, se ele não tá replicado dentro do Elemento?
Olha aqui a solução aqui, ó.
Então a solução posta foi eliminada
riscada com vocês tão vendo aí.
E aqui eu tenho um pauxAl
tenho também um pauxEl
todos aterrados com nulo.
Esse pauxEl
obviamente é um ponteiro Elemento parametrizado com Aluno.
Esse pauxEl, naturalmente, vai apontar pro primeiro.
Enquanto esse pauxEl não estiver apontando pra algo nulo,
ele vai indo de próximo, próximo, próximo.
Cada vez que ele estiver numa passagem de loop,
o Elemento vai fazer um getInfo, ou seja,
o pauxEl que permite navegar em cada Elemento da lista,
cada Elemento da lista,
quando ele estiver no primeiro Elemento, que vai ser a primeira passada, o que vai fazer?
Ele vai recuperar o getInfo
desse primeiro Elemento Aluno por assim dizer das lista.
E o que que esse getInfo retorna?
Ué, pra que que ser um Elemento Aluno?
Pra cuidar de Aluno na lista.
Então, se eu tiver um cara que tá acessando
um Elemento Aluno como é o caso desse ponteiro aqui, pauxEl.
O que acontece se eu fizer um getInfo?
Olha, eu tenho o objeto Elemento Aluno
o Elemento parametrizado com Aluno,
pra que esse cara existe? Ele existe pra cuidar da informação
do parâmetro, ou seja, do Aluno.
Ah, então, seu fizer um getInfo
do meu Elemento parametrizado com Aluno ele vai me retornar o Aluno?
Justamente isso que ele vai fazer.
O getInfo vai retornar o Aluno.
E aí, eu vou ter acesso ao Aluno,
e em tendo acesso ao Aluno,
eu vou lá e faço diretamente do Aluno a impressão do seu nome.
Se eventualmente eu trocar o nome do Aluno
a próxima vez que eu acessar, eu vou acessar direto o Aluno em questão
já com o nome devidamente trocado.
Professor, eu não tô convencido que esse getInfo retorna o Aluno.
Vou voltar uma classe.
Aqui, ó.
Classe Elemento.
Tem o tipo, no nosso caso lá é Aluno.
Aluno, então o pInfo é ponteiro de Aluno.
O setInfo recebe ponteiro de Aluno,
o getInfo retorna o quê? Ponteiro de Aluno.
Tá ali, ó.
Aqui, o setInfo põe a informação em um pInfo
põe o apontamento em pInfo, né.
O apontamento do Aluno em pInfo.
Mas como é que ele pode saber que é um Aluno?
Por causa do parâmetro que tá vindo, parâmetro é Aluno.
Ponteiro de Aluno.
Aqui ó o asterisco na frente, ponteiro de Aluno.
Ele pega e armazena esse ponteiro de Aluno, lá no pInfo.
Na situação posta, vai ser ponteiro de Aluno por causa do parâmetro dado.
E aí, a hora que eu fizer o getInfo, vou retornar pInfo,
que no contexto dado vai ser ponteiro de Aluno.
Então, daí, isso aqui funciona corretamente.
É claro ele vai sair do primeiro Elemento
do segundo através do getProximo,
e lá ele vai recuperar a partir do getInfo
o segundo Aluno,
ele vai imprimir o nome do segundo Aluno e assim vai até findar
a navegação da lista por meio dos elementos parametrizados com Aluno
cada um dali gerenciando cada Aluno que foi inserido na lista.
Aqui a gente pode retomar um exercício anterior.
Cada Departamento deve ser capaz de armazenar
uma lista de Disciplinas.
A classe Disciplina, entretanto,
não deverá possuir um ponteiro para o Próximo.
Isto deverá estar em uma classe associada chamada ElDisciplina,
relacionada a LDisciplina ou ListaDisciplina.
Certo.
Aqui tem um segundo momento do exercício.
Muito que obviamente utilizar o gabarito
template class Elemento pra resolver o exercício em questão.
Aqui é basicamente o seguinte, pessoal,
eu quero que vocês façam uma pausa no vídeo
e vocês constituam os códigos de vocês,
criem uma nova versão dos códigos que vocês tão fazendo
criem uma classe gabarito, lá, só .h em Elemento.
Elemento vai ser template com class TIPO.
Façam a implementação que eu mostrei pra vocês, ali.
Peguem aquele exercício que vocês já tem
da classe ListaDisciplina
que era animado internamente por um ElementoDisciplina,
e agora substituam devidamente
por um Elemento parametrizado com Disciplina,
e façam os testes.
Claro, né, muita atenção na hora de utilizar, né,
porque você vai ter que incluir aqui, tá, no Elemento.h.
Não esqueça de incluir Elemento.h.
Não vai ser mais ElementoAluno,
nem ElementoDisciplina, nem ElementoAlgo
vai ser simplesmente Elemento, porque a classe agora é genérica.
Não é isso?
Tudo isso dito,
faça a pausa no vídeo
e faça o exercício
e já já, uma vez feito o exercício, vocês voltam aqui
ver a sequência dessa aula.
É importante que você para e faça o exercício,
porque se não, dificulta a compreensão subsequente do que eu tenho pra falar.
Supondo que você já fez o exercício
então avançamos para uma segunda reflexão,
ou pra continuação da reflexão posta até então.
A solução utilizando o gabarito template
class TIPO class Elemento,
facilita a composição de listas em classes.
Ou em cada classe pertinente.
Não é isso?
Mas o tratamento de inclusão e consulta
da lista em cada classe
que necessita de uma lista parece-lhes uma solução inteligente ou mesmo razoável?
Não seria melhor criar uma classe Lista genérica
para tratar disto? Ou parte disto?
Essa é uma questão muito interessante, sabe.
Se você der uma olhadinha lá, por exemplo, nos algoritmos de inclusão,
eles são muito iguais.
O algoritmo de exclusão que tem na destrutora eles são muito iguais.
Se você vier aqui na classe ListaAlunos
que a gente já expôs aqui nessa apresentação
e você olhar a classe que você fez, aí,
ListaDisciplinas agora usando Elemento parametrizado com Disciplina.
As funções de inclusão
da ListaDisciplina e da ListaAluno são muito parecidas
o que muda é que uma tá criando Elemento parametrizado com Disciplina
e a outra tá criando Elemento parametrizado com Aluno
e os acessórios pra isso.
É basicamente isso que muda.
E aí, vem a reflexão se eu não posso generalizar.
E até já deixando uma reflexão maior aqui,
se essa generalização ela pode ser completa ou só parcial.
Vamos discutir isso aqui agora nessa aula que tá rolando aqui.
Sim, eu posso generalizar.
Se quem fez a STL generalizou classes.
Vocês têm o List, tem o Vector que são parametrizados com algo,
nós também vamos generalizar a nossa lista aqui.
A gente não vai mais precisar,
pelo menos no tocante à parte genérica,
de criar listas específicas como ListaAlunos,
ListaDisciplinas, ListaUniversidades e afins,
tratando coisas que são muito parecidas,
aliás que são idênticas e que só muda a parametragem.
Aqui, eu tô criando uma classe List
que como vocês tão vendo é um template de class TIPO.
Inclusive, esse template aqui
vai se relacionar, vai ser baseado
em outro template que tá aqui no Elemento.h,
conforme vocês já estão proficientes suponho, porque fizeram o exercício anterior.
Aqui tem construtora e destrutora.
Você tem aqui uma função inicializa, a gente já vai ver do que se trata.
Tem um incluaElemento e um incluaInfo.
Veja que, via de regra,
a gente só tinha um incluaAlgo,
incluaAluno, incluaDisciplinas, incluaIsso, incluaAquilo,
agora a gente tá generalizando, incluaInfo.
Só que a gente tem além do incluaInfo, a gente tem então o incluaElemento
daqui a pouco a gente já fala quais são as diferenças entre os dois.
Temos o listeInfos ainda.
Vejam que aqui eu tenho a definição da classe,
depois tenho a implementação das funções, cada uma delas,
das funções membro.
Insisto, isso aqui é só no .h, gente.
Não tem .cpp,
pelos motivos já explicados.
Só .h
e ainda assim no .h eu tô separando
em definição e implementação
pra fins de organização, tá,
mas ele fica tudo no .h.
Método definido no .h
faz com que cada objeto tenha um método pra si
e que os objetos não compartilhem os métodos.
Entre supostos iguais, criados a partir de sua classe.
No caso do template,
nem sempre os objetos vão ser iguais, porque eles,
justamente, dependem de parâmetros.
Aqui no nosso caso dependem do parâmetro TIPO
e é por isso inclusive que a coisa tem que ficar tudo no .h.
Aqui no .h
você tem, por exemplo, a implementação da inicializa,
que coloca null no pPrimeiro e no pAtual
e esse inicializa é chamado aqui na construtora.
Professor, mas porque que eu já não boto esse código direto?
Talvez eu queira aterrar em algum outro lugar o pPrimeiro e o pAtual.
Por exemplo, aqui, ó.
No código de desalocação que eu deixei aqui de exercício pra vocês fazerem.
Depois de desalocado, eu posso chamar de volta o inicializa
pra aterrar os ponteiros.
Ponteiro aterrado é sempre uma boa coisa.
Bom, aqui eu tenho o incluaElemento
vejam que aqui, ó, eu não tô incluindo a informação
aqui é o Elemento que vai tratar da informação.
Se for um Elemento parametrizado com Aluno,
então essa função que recebe um Elemento parametrizado com Aluno
e vai ver primeiro se ele não é nulo
aí, ele vai ver se ele é o primeiro,
se for primeiro vai fazer o amarramento com primeiro e com atual,
senão ele vai fazer os amarramentos pertinentes.
Aqui, tá, eu comentei um código que não era necessário, aqui, tá.
Não tem problema também, ele funciona se colocar, se descomentar, mas
eu comentei porque era prolixo.
Então, aqui, eu tenho uma função incluaElemento
que recebe um Elemento, criado em algum lugar,
e amarra esse Elemento na lista.
Aonde que esse Elemento é criado?
Ora, aqui eu tô em incluaInfo.
Que informação que eu tô recebendo? Depende.
Se a lista for parametrizada com Aluno,
então o incluaInfo vai ser aqui ponteiro de Aluno.
Esse pInfo seria equivalente a um pAluno, né,
mas é pInfo o nome dele que vai ser um ponteiro de Aluno
supondo que o parâmetro é Aluno aqui, insisto.
Eu tenho o segundo parâmetro aqui, que é nome.
Tá vendo, ó. Nome.
Por quê? Vamos descobrir.
Testo se o ponteiro não é nulo.
Crio o pElemento.
Poderia tranquilamente estar aterrado aqui com nulo
boa prática, já discutido.
Muito embora nesse contexto dado,
não é necessário por um motivo evidente.
Logo em seguida eu já vou utilizar o ponteiro pElemento.
Aqui sim que eu tô criando um novo Elemento.
Crio o novo Elemento, passo alguns dados pra esse Elemento.
Replico o nome dentro do Elemento.
Insisto, replico o nome que tá vindo por parâmetro dentro do Elemento.
Sem juízo de valores se isso é bom ou ruim por enquanto.
Linha subsequente,
eu faço o Elemento conhecer a informação que ele vai tratar.
Se for um Aluno, então ele vai conhecer o ponteiro do Aluno.
E aqui eu faço o incluaElemento.
Eu tô passando o Elemento que eu criei.
Claro, né, passando o endereço do Elemento que eu criei, bem entendido,
por isso tô passando um ponteiro.
Ah, tá, professor, então em vez de fazer aquele teste se é primeiro
de se é primeiro ou se não é primeiro e coisa e tal
e fazer a amarração de próximo e anterior e tudo mais, aqui dentro.
O senhor colocou isso em um outra função? Foi, foi isso.
Pra deixar um pouco mais organizado.
E a função, tá no slide anterior, que a gente já discutiu.
Recebo o Elemento,
faço o teste, aqui, de consistência, pra ver se o ponteiro não é nulo.
Testo se é o primeiro, se for o primeiro
eu faço amarrações de primeiro de atual,
senão eu faço as amarrações de anterior e próximo
e atualização de atual.
Aqui, na inclusão do endereço que vai ser tratado dentro da lista,
é feito no incluaInfo,
recebendo o endereço por meio de um ponteiro.
Testo se o ponteiro, obviamente, não é nulo.
É um teste que a gente tem que fazer pra fins de consistência.
E aí, não sendo nulo, ou seja, se realmente tá vindo uma coisa,
um endereço pra ser tratado, né, nesse âmbito,
aí eu vou criar um Elemento, ponteiro de Elemento, né,
pra ponteiro Elemento do tipo apropriado.
Vou criar o objeto Elemento em questão.
Dentro do Elemento, eu vou armazenar um nome que tá vindo por parâmetro
e no Elemento também eu vou armazenar,
através do ponteiro, eu vou armazenar meu Elemento criado.
Esse Elemento que foi criado ele tá apelidado temporariamente de pElemento.
Eu vou colocar, no Elemento criado, através do apelido dele,
o setInfo, ou seja, colocar
o endereço do cara que eu quero, justamente, tratar por meio do Elemento criado.
Uma vez que eu tenho um Elemento pra cuidar do que tá vindo por parâmetro,
que é o pInfo,
eu incluo o Elemento efetivamente na lista
que é amarração de próximo, anterior e tudo mais que vocês sabem.
Retorna um true se deu boa e retorna um false se deu ruim,
em função do ponteiro ser nulo ou não.
Provavelmente ele não vai ser nulo.
Pelo menos não no contexto que a gente tá trabalhando.
Aqui eu tenho um listInfos.
Bom, incluir um cara na lista, via de regra, é, de fato, um algoritmo genérico,
porque eu não dependo do tipo dele pra incluir ele na lista.
Não dependo, no sentido de precisar de detalhes do tipo.
Só preciso saber que é um tipo. Tá aqui, ó.
Preciso saber que é um ponteiro de algo.
Eu pego esse ponteiro e armazeno em um tratador que é o Elemento
e tá resolvido.
Eu não dependo de informações específicas
do que tá vindo nesse tipo.
Já na listagem é um pouco diferente.
Uma listagem pra eu imprimir coisas
eu tenho que saber detalhes do tipo.
Supostamente eu deveria saber.
Por exemplo, nome, RG e CPF se for um Aluno.
Se for uma Universidade, nome, CNPJ,
índice de qualidade da Universidade e por aí vai.
Bom, e como é que eu faço uma listagem genérica,
se eu não tenho essas informações?
Um estratagema, não muito elegante, inclusive,
é colocar alguma informação replicada dentro do próprio Elemento.
Aqui, por exemplo, eu tô armazenando nome, mas ele ainda é algo dentro do Elemento.
E aí, genericamente, eu vou navegar aqui na minha lista,
através de um ponteiro auxiliar, sempre indo pro primeiro.
Enquanto o primeiro não for nulo,
eu vou de próximo, em próximo, em próximo,
imprimindo o que tem em getNome.
Bom, é uma maneira de resolver a questão da generalidade
replicar informações dentro do Elemento,
mas a gente sabe que replicar informação não é uma boa coisa
conforme eu já expliquei ali, na explanação anterior.
Aqui, por exemplo, se eu tiver replicando o nome de um aluno,
e depois o aluno for atualizado, em seu nome, lá na sua origem.
Eu vou ter que dar um jeito de sincronizar as coisas aqui
e isso aqui vira uma dependência.
Cria um problema grave, tá.
Porque a gente tá violando coesão e desacoplamento.
Cria um acoplamento aqui, isso aqui é acoplante,
mas foi assim que foi feito.
Uma solução pelo fato de ela ser acoplante, não significa que ela não funcione.
Ela pode funcionar.
Só que, não necessariamente com a
qualidade nem a manutenibilidade que a gente gostaria,
a título de exemplo.