Pessoal, boa tarde.
Hoje é uma segunda-feira,
do dia 3 de julho de 2020.
Estamos aqui pra falar do Grupo de Slides número 7 do nosso curso.
Aonde a gente tratará de Arquivos e Persistência de Objetos, né.
E quem fala é o professor Jean Marcelo Simão.
Vou colocar em Modo Exibição.
Aí está.
Vou passar pro slide número 2
desse Grupo de Slides, né, em questão.
E, de fato, vamos falar de Persistência de Objetos
por meio de arquivos via fluxo
aonde a gente trata das bibliotecas
ofstream e ifstream.
Output File Stream e Input File Stream.
Ofstream e Ifstream, né.
Que são os nomes em inglês aí pra essas duas bibliotecas.
Então, isso considerado,
ainda, uma informação importante
que você encontra esses conteúdos de Arquivos no livro do Deitel.
Na versão antiga, provavelmente a penúltima, no Capítulo 14.
Deve ter evoluído na última versão do livro dos Deitel, tá.
Antes de mais nada,
eu tenho que fazer uns pequenos ajustes aqui na classe Aluno
pra que a gente possa alcançar
o que foi solicitado como exercício lá na aula passada.
Na aula passada, na exposição passada a gente pedia que cada classe, né,
pertinente, particularmente, a classe base tivesse um Id, tá.
Pra que eu possa identificar cada Pessoa de maneira única no sistema.
Bom, aqui tá a classe Aluno, derivada de Pessoa.
Ele tem ali o Registro Acadêmico.
Tem aqui, alguns códigos que eu não tô mostrando.
Como a construtora com mais parâmetros e tem uma construtora aqui,
equivalente à construtora sem parâmetros, que tem o atributo i com default de -1.
Se você olhar lá a implementação,
cê vai ver que esse i alimenta o Id
e o RA colocado como zero,
mesmo porque depois eu tenho o set e get RA.
Ué, mas da onde que vem esse Id?
Se ele não está aqui, só pode estar na classe Pessoa.
A classe Pessoa tem que ter um Id lá,
protegido, inclusive, pra eu ter acesso direto aqui.
Bom, isso aqui não tá muito bonito, tá,
porque se é a classe Pessoa que tem o Id
é essa classe que tem que inicializar.
Por isso que o próximo slide tá mais correto,
que é esse que vocês tão vendo aí slide número 5.
Aqui eu tô pegando o i
que vem na construtora de Aluno
e repassando pra construtora de Pessoa.
Obviamente que daí a construtora de Pessoa lá
tem que suportar isso, bem entendido.
Mas isso é mais ou menos evidente suponho.
Lembrar que esse espaço aqui entre o nome
da função construtora e sua implementação
servem pra chamar a construtora de classe base
ou chamar a construtora de objeto agregado.
Aqui, o que eu tenho como objeto agregado? Eu tenho o RA,
que é um objeto da classe inteiro, uma classe preexistente.
Veja o próximo slide, tá. Em vez de eu inicializar o RA assim,
eu posso chamar a construtora da classe inteira.
Olhe lá.
Isso funciona tranquilamente, muito elegante.
Esses detalhes, aqui, vão ser importantes pra algumas mudanças que eu vou fazer na classe Principal
e essas mudanças são pertinentes pra que a gente possa fazer
O que que é persistência de objetos mesmo? Diga-se de passagem.
É quando um objeto ele persiste às execuções do sistema.
Então, eu posso abrir e fechar o sistema várias vezes e, quando eu volto, o objeto tá lá de alguma maneira.
E a gente vai fazer isso por meio de arquivos.
Classe Principal.
Vamos fazer mudanças, então, como eu dizia na classe Principal.
pra poder suportar a persistência de objetos.
O que que mudou aqui?
Bom, aqui a gente tem a classe Principal com as coisas que vocês já conhece.
Alguns comentários. Leiam com atenção.
Aqui eu criei contadores pra identificadores, ou seja,
já que cada objeto vai ter um identificador único
criei ali ó, um contador de Disciplinas
um contador de Departamentos, um contador de Id de Alunos, né, de Aluno.
Para que eu possa gerar um número único para cada objeto pertinente.
Cada Aluno vai ter seu número.
Vai ter o Aluno 1, 2, 3, 4 e por aí vai.
Cada Departamento vai ter um número também.
1, 2, 3, 4 e por aí vai.
Aqui tem as Universidades que cês já conhecem,
tem os Departamentos que cês já conhecem.
Algum código escondido, ali, que seriam mais instancias de Departamento quiçá.
Tenho aqui Professores, tenho Disciplinas.
Escondidinho aqui tenho int diaAtual, mesAtual, anoAtual.
Tenho os Alunos e aqui tem as listas.
A gente já tinha criado, na aula anterior,
o LUniversidades que é uma lista de Universidades;
o LDepartamentos que é uma lista de Departamentos;
o LDisciplinas que é uma lista de Disciplinas.
Agora, eu acrescentei mais uma.
Criei o LAlunos que é da lista de Alunos.
Por quê? Porque vai ser bastante útil pra demonstrar o que eu quero demonstrar.
Aqui tem a construtora, o Inicializar que chama vários inicializares que estão aqui escondidos.
Tem aqui o Executar que chama... pode chamar, né,
e, normalmente, chama algumas funções
iniciais, nossas lá, que a gente criou,
quando começou a compor a coisa só com objetos estáticos.
Depois a coisa evoluiu pra ter... cadê? Um menu.
Chamado a partir da função Executar também
Esse menu permite a pessoa escolher entre
Cadastrar, Executar e agora, permite também, Gravar e Recuperar.
Não é igual na aula passada que só tinha duas possibilidades,
agora tem três, aliás, quatro possibilidades.
Aqui eu tenho as funções que vocês já conhecem da aula passada.
CadDisciplina, CadDepartamento
CadUniversidade pra fins de cadastro, né.
E tem, ademais, agora, o CadAlunos.
Tem agora: GravarTudo; GravarUniversidades; GravarDepartamentos; GravarDisciplinas;
GravarAlunos e GravarProfessores.
Assim como tem as funções recuperar também.
RecuperarTudo, RecuperarUniversidades e por aí vai.
Pra ser sincero,
as funções que estão, de fato, implementadas são essas que tão sublinhadas aqui, tá.
As demais, vão ficar como exercício de vocês pra vocês implementarem,
conforme vai tá no final desse Grupo de Slides.
Aqui eu tenho a construtora da Principal, não tenho?
Eu fiz, agora, na construtora
um código um pouco peculiar que eu vou mostrar no próximo slide
que é o slide número 9.
Aqui, entre o nome da construtora, aqui no .cpp,
e a sua implementação que tá aqui embaixo,
eu tô inicializando alguns objetos, e eu posso, porque esse espaço permite
que eu inicialize objetos agregados,
como é o caso aqui,
ou chamar a construtora de classe base
que não é o caso aqui, porque Principal não tem classe base.
Aqui eu tô inicializando os contadores de Id's, né.
Aqui eu vou salientar o cont_id do Aluno que recebe 0.
E aqui eu inicializo,
utilizando inclusive os cont's, tá,
outros objetos, eu vou salientar aqui o que tá em sublinhado
que eu tô, por exemplo, colocando no triplo A o valor 0.
E no retorno da função, eu tenho um cont_idAluno++,
o que faz com que o B na próxima receba 1.
No retorno, esse cont Aluno vai virar 2,
que faz com que o triplo C receba 2, enfim , vai somando.
Você começa com 0, né.
Então, esse recebe 0, esse vai receber 1, esse vai receber 2, esse vai receber 3, esse vai receber 4,
por causa do ++ aqui, que é executado no retorno da função.
Veja que o ++ tá atrás.
Se o ++ tivesse na frente
ele ia somar antes de entrar na função,
mas aqui a somatória ocorre depois que ele tá retornando da função.
É um artifício aqui do C++
utilizar esse operador de ++, justamente.
Chamada aos construtores dos objetos agregados explicitamente na classe Principal.
Isto é, dos atributos da Principal que são objetos de
alguma outra classe criada no sistema.
Como de fato.
Atenção.
A ordem de chamada dos construtores dos objetos, agregados, nesta classe
é definida pela sua ordem de declaração na classe Principal,
lá no .h,
e não pela sua ordem de chamada aqui,
a partir do construtor Principal.
Então, por exemplo
se colocar o B antes do A,
mas, se aqui nesse código eu colocar o B antes do A,
mas eu voltar lá, na definição,
o B vem depois do A,
ele vai chamar A, B, C e D... ele vai chamar conforme tá aqui, tá,
e não conforme eu tô definindo aqui.
Isso é um negócio muito peculiar.
Melhor tomar cuidado.
Mas, enfim, por que eu tô fazendo isso mesmo?
Pra inicializar objetos, tá.
Na verdade, podia, e deveria,
chamar todas as construtoras aqui, tá.
Quando eu não chamo a construtora
Ele vai chamar a construtora sem parâmetro
de maneira implícita, tá,
sem você, supostamente, perceber,
mas você que sabe agora, você percebe no sentido de saber que...
que vai ser chamado a construtora sem parâmetro aí.
Se eu não chamei uma construtora aqui, de algum objeto
agregado na classe Principal,
ele vai ser chamado de maneira automática, nesse âmbito aqui, e a gente não tá vendo.
O ideal seria explicitar tudo, tá.
Muito embora, não é todo programador que explicite,
mas, insisto, seria uma boa prática.
Aqui, tá o menu que vocês já conhecem, só que agora ele aumentou com duas opções.
O Gravar e o Recuperar.
E aí, com dois casos a mais obviamente.
O MenuCad, que vocês já conhecem,
aumentou aqui com uma opção que é o Cadastrar Aluno
que chama a função CadAluno.
O MenuExe, agora, tem uma opção a mais que é o Listar Alunos
a partir da lista... da LAlunos que é uma lista de Alunos.
O MenuGravar, que vocês não conheciam, é um menu novo.
Um menuzinho sem nada de extraordinário, tá.
Aqui, é conforme as opções, eu chamo alguma dessas funções.
Na verdade, quem tem código de fato,
aqui só a opção 4 que é a Gravar Alunos, tá.
Pelo menos essa versão que vocês tão
trabalhando agora, aqui, que a gente tá trabalhando agora
MenuRecuperar.
Mesma coisa, tá.
Tem várias opções ali de recuperação
Tem aqui o 4 que é aonde realmente tem código
nessa versão do sistema que eu tô apresentando pra vocês.
Então, se o que interessa pra nós é o Gravar Alunos e o Recuperar Alunos,
vamos salientar isso no próximo slide.
Tá aqui, ó.
O que que o GravarAlunos faz?
Ele vai lá no LAlunos
e chama a função graveAlunos.
Opa, professor, então, quer dizer que o senhor andou
modificando a lista, andou criando uma nova função na lista?
Sim.
Foi na lista que eu criei uma lista de gravar Alunos.
E por que na lista?
Porque é na lista que conhece os Alunos.
Se a lista conhece os Alunos,
uma opção que eu tenho é fazer a função graveAlunos, tá.
Eu até poderia criar uma classe
derivada de ListaAlunos que seria ListaAlunosGravadora
e aí fazer o LAlunos ser instância da ListaAlunosGravadora
pra aumentar um pouco o nível de desacoplamento.
Mas, ainda sim,
o graveAlunos estaria relacionado a uma lista de Alunos
mesmo que especializada.
O recupereAlunos ali, ó.
Também, tá. Também foi feito na lista.
Por quê?
Porque daí,
enquanto nessa função, você vai percorrer a lista
recuperando as informações e jogar em um arquivo
aqui você vai percorrer um arquivo, recuperando as informações e jogar na lista.
Ainda tem a função CadAluno que é uma função nova, né.
que não tem muito mistério, ela vai perguntar o nome do Aluno,
colocando esse vetor de caracteres.
Pergunta qual é o RA do Aluno, colocando nesse inteiro.
Vai através de um, ou por meio, de um ponteiro temporário
registrar aqui a criação de um novo objeto Aluno
que vai receber o cont_idAluno, né.
Se ali o cont_idAluno tiver em 4,
então ele vai receber o 4.
Depois ele vai incrementar o cont Aluno, bem entendido.
Obviamente, que o próximo Aluno criado já vai receber o 5, por exemplo, tá.
Aqui, através desse apelido,
nesse âmbito aqui da função,
eu chamo a função setNome do Aluno criado, passando o nome.
Chamo o setRa do Aluno criado, passando o RA.
E depois pra não perder esse endereço que foi criado aqui...
Professor, mas esse endereço não vai ser perdido, porque eu tenho pal.
pal é uma variável temporária, hein.
É por isso que eu pego o endereço que tá registrado em pal,
não o pal ele mesmo, mas o registro que tá nele... o endereço que tá nele.
e armazeno esse endereço lá na lista de Alunos geral pra não perder o endereço.
Bem entendido.
Vejam que esse código que tá aqui, tá.
Prestem atenção nessas duas linhas.
Ele é equivalente ao código do próximo slide.
Mesma coisa, só que uma versão mais sucinta,
mais econômica, né,
mas faz o mesmo efeito, tá, só pra constar.
Muito bem.
Agora, sim, depois desse preparo todo,
a gente pode falar um pouco de persistência dos objetos da classe Aluno,
a partir de LAlunos, né, da lista de Alunos
existente na classe Principal,
ou os objetos existentes na classe Principal.
Ali, a gente vai falar sobre a utilização de arquivos
na orientação a fluxo, né.
Usando o operador de fluxo, igual o cout trabalha com fluxo, né,
e, no nosso caso, a gente vai falar de arquivos sequenciais ou de texto, tá.
Muito bem.
Aqui tem as duas funções, tá, que a gente vai utilizar.
Na ListaAlunos a gente cria uma função graveAlunos e uma recupereAlunos.
Eu vou destacar, nos próximos slides, essas duas funções em particular.
Tá aqui a função graveAlunos, tá.
Aqui eu tô criando um objeto.
É um objeto como se fosse um acesso
entre aspas, um "mnemônico", na verdade, um caminho, tá.
É um objeto que me dá acesso a algo.
Que algo é isso?
É um arquivo.
Eu vou ter no meu HD
normalmente no diretório a partir da onde tá rodando o meu executável,
eu vou criar um alunos.dat.
Esse é um arquivinho .dat de dados, né.
É um texto, na verdade. É um arquivo de texto.
Então, esse objeto aqui, permite a gente acessar esse arquivo ali, tá.
Se esse arquivo não existir, ele cria pra você.
Nesse formato que tá aqui, de saída,
mesmo que ele exista, ele vai apagar o que já tem e abrir um novo.
Esse objeto é do tipo ofstream.
Output file stream.
Arquivo de saída via fluxo.
Aqui, nesse objeto, ele suporta,
porque foi programado pra isso, o operador de não (!).
GravadorAlunos significa que
eu não consegui abrir esse arquivo.
Professor, mas como é que vai dar erro?
Sei lá, por exemplo, eu tô rodando num sistema que só tem RAM, não tem HD, né.
Não tem Hard Disk.
Eu tô com meu HD lotado.
Eu tô com meu HD danificado.
Então, pode dar erro.
Aí, se ele não conseguir abrir o arquivo, ele testa aqui
Daí, se ele não conseguir ele vai então, né,
dar uma informação aqui que é "Arquivo não pode ser aberto";
Esse cerr aqui, é o cout de erro.
Inclusive, se não me falha a memória, tem como configurar aqui
a cor do texto que sai lá no console
pra vermelho, pra outra coisa, tá. Mas enfim,
o cerr é equivalente ao cout só que só imprime erro.
Muito embora, muita gente acabe utilizando o próprio cout mesmo,
mas o fato é que tem um objeto similar ao cout
que é o cerr pra imprimir erro.
Você que o cout e o cerr, eles são objetos
ambos da classe ostream
output stream, fluxo de saída,
não da classe ofstream,
que é aparentada dessa, mas é outra.
Output file stream que é arquivo de saída por fluxo.
Aqui, na verdade, via de regra, ele não vai entrar nesse if, tá.
Vai dar tudo certo, não vai dar erro, ele não entra no if.
Aqui eu tô criando um ponteiro auxiliar de Elemento Aluno,
ponteiro de ElAluno, obviamente,
aterrado com nulo,
Tô criando um pauxAluno aterrado com nulo, que é ponteiro de Aluno.
Aqui, eu tô fazendo o paux, o ponteiro auxiliar,
pra ElAluno apontar pro primeiro ElAluno na lista.
Tem que lembrar aqui, gente, que a gente tá numa lista de Alunos, tá.
Então, você tem, na verdade,
cada vez que você inserir um aluno na lista, foi criado um Elemento pra cuidar dele na lista.
Aqui eu tô recuperando o primeiro Elemento
no paux aqui, eu tô recuperando o primeiro Elemento
que foi criado pra cuidar do primeiro Aluno inserido na lista.
Eu ia lembrar que isso aqui é uma lista geral,
ou, no final das contas, tá,
quando a gente for utilizar isso aqui, a gente vai utilizar na lista geral
de Alunos do Principal.
Como eu fiz isso daqui na classe ListaAlunos,
qualquer lista que for criada no sistema
pode gravar os seus Alunos no seu âmbito.
Tomando cuidado pra não gerar conflito com o nome do arquivo.
Isso pode gerar conflito, porque todos vão trabalhar com o mesmo arquivo, tá.
Se você não quer gerar conflito com o nome do arquivo,
então passa o nome do arquivo por parâmetro ali
e reutiliza aqui.
Mas enfim, o fato é que
essa função graveAlunos, por ora, eu tô utilizando apenas na lista geral
de Alunos do Principal, que é o LAlunos.
Isso considerado,
insisto, que o que eu tô fazendo aqui
é fazer com que o meu pauxElAluno
aponte pro primeiro ElAluno que foi criado
pra tratar de Aluno na lista.
Enquanto o pauxElAluno não for nulo,
ou seja, eu vou navegar nos elementos.
Vou navegar nos elementos pra quê?
Pra acessar cada um dos Alunos que cada Elemento trata, ué.
Então, você veja, que é justamente isso que eu tô fazendo aqui, ó.
Aqui eu tô recuperando, na primeira passagem do loop,
tô recuperando o primeiro Aluno tratado na lista pelo primeiro ElAluno
e tô recuperando esse endereço do Aluno, né,
esse apontamento do Aluno e armazenando nesse ponteiro temporário.
A partir do momento que eu tenho acesso ao Aluno
eu posso chamar suas funções públicas,
getId, getRa e getNome.
Se aqui fosse um cout, o que ele ia fazer?
Ia imprimir em tela, né.
Mas como não é um cout, gente, é um arquivo,
ele vai gravar no arquivo,
um arquivo de texto.
Aí, eu pulo linha.
Passo pro próximo elemento.
Dou loop.
Recupero o segundo Aluno.
Gravo o conteúdo do segundo Aluno no arquivo.
Passo pro próximo, que é o terceiro elemento.
Dou loop.
Se tiver um terceiro elemento,
então, ele vai entrar aqui. Vai recuperar o terceiro Aluno.
Vou gravar as informações do terceiro Aluno num arquivo e assim por diante.
Assim que funciona.
Depois que ele terminar de percorrer a lista de Alunos
através dos elementos alunos, ele vai acabar uma hora finalizando esse percorrimento
e vai fechar, depois de terminar o loop, vai fechar, aqui,
o arquivo, através do comando close desse objeto.
Esse comando close aqui, desse objeto...
esse comando close desse objeto, vai fechar lá o arquivo alunos.dat,
pra não deixar ele em aberto.
Muito bem.
Isso aí é o sistema de gravação
via fluxo usando arquivos de texto.
Agora, eu posso ter também, a leitura.
Ora, se aqui eu gravei,
então, o que que eu fiz aqui?
Eu fiz percorrer a lista e gravei
em cada linha do meu arquivo
as informações de um Aluno.
Se eu gravei eu posso recuperar.
Como é que eu faço a recuperação?
Crio um objeto pra acessar o alunos.dat.
Só em formato de leitura, de entrada.
E esse objeto é do tipo ifstream,
input file stream,
arquivo de entrada via fluxo.
Testo se não deu ruim na leitura.
Sei lá, arquivo corrompido.
Geralmente não vai dar, tá.
Aqui é uma funçãozinha limpaLista.
que eu criei dentro lá da... da lista.
Porque o limpaLista faz o seguinte, tá.
Ele entra e zera lista,
se tiver alguma coisa na lista ele limpa.
Não deixa mais nada lá.
Deixa a lista zerada pra evitar confusão.
Aqui eu faço um while, que é
enquanto não for final de arquivo,
ou seja, quando você abre o arquivo,
que é o caso aqui,
ele abre do começo do arquivo.
O apontamento de arquivo tá no início do arquivo.
Enquanto não for end of file,
enquanto não tiver no final do arquivo, ele fica rodando o loop.
Aqui eu tô criando um cara auxiliar aterrado com nulo,
algumas variáveis que me são úteis.
Eu vou, através do arquivo de fluxo, tá.
Imagine se aqui fosse um cin.
Se aqui fosse um cin,
ele ia ler o que tá sendo digitado no teclado pra id, RA e nome, né.
Ia digitar, Enter. Ia digitar, Enter. Ia digitar, Enter e ele ia ler aqui, tá.
Aqui, ele não é um cin, aqui é o arquivo de fluxo.
Então, ele vai ler o que tá na linha
usando os espaçamentos
pra poder recuperar, aqui, as informações.
Aqui eu tô testando se não é a última linha, porque a última linha fica em branco.
Não tem dados, tá.
Fica um vazio, né.
Se não for a última linha, o que eu faço?
Eu recomponho, eu crio um novo Aluno,
em tempo de execução,
e ponho nesse Aluno os dados que eu recuperei lá do meu arquivo.
E aí, incluo esse Aluno na lista.
Assim eu tô fazendo
o renascer de um Aluno que já existiu
numa outra rodada.
Claro que aqui é um novo objeto, uma nova área de memória, mas
com os mesmos dados que já tinham no outro objeto anterior.
Sendo que pro usuário fica transparente,
pro usuário ele cria aparência de persistência, como se o objeto estivesse sempre lá.
Os mesmos dados que estão lá, bem entendido,
e os objetos são recriados pra tratar os dados em tempo de execução,
como vocês tão vendo.
Depois que eu recuperar todo mundo,
eu posso fazer um close aqui no arquivo, né,
pra não deixar ele em aberto.
Aqui tem uma outra versão da função recupereAlunos, tá,
que não usa o end of file.
Usa, ali, o while
enquanto ele conseguir fazer essa operação aqui, tá.
Vai chegar no final do arquivo e ele não vai conseguir fazer essa operação.
Quando ele não consegue fazer essa operação, ele dá um false e sai do while.
É outra maneira de fazer a mesma coisa.
Vocês podem olhar depois e testar essa solução
com a devida atenção.
E aqui vocês, então, tem a possibilidade de
gravar e recuperar informações.
Aqui tem um exercício.
Deixa o exercício aí, daqui a pouco eu venho falar dele.
Eu quero, antes, mostrar a coisa rodando.