Bom dia, pessoal.
Hoje é dia 12 de agosto de 2020,
uma quarta-feira, 10h43 da matina.
Estamos aqui hoje para falar do grupo de slides número 14.
Esse grupo de slides trata sobre List e Vector da STL,
que eu já falei, inclusive, em aulas passadas,
mas agora eu trago umas propriedades a mais,
buscando mostrar a riqueza de recursos que existe
nesses componente List e Vector da Standard Template Library, da STL
que é a biblioteca padrão de templates, bem entendido.
Começamos a falar sobre a classe List,
que é uma classe predefinida na STL
e saliento que os exemplos são adaptados do livro dos Deitels,
pai e filho Deitel, "C++ como programar", terceira edição lá da Bookman, de 2001.
Têm versões mais novas, bem entendido,
só que esse grupo de slides em particular foi inspirado nessa edição desse ano.
Vocês sabem que como a gente programa em Microsoft Visual c++
eu tenho esse recurso do stdafx,
Standard affixment, que a gente já discutiu em aulas precedentes.
Basicamente esse stdafx permite você concentrar
em um único .h bibliotecas externas.
Aqui, por exemplo,
ele tem essas que vem inclusas
stdio, tchar, que vem inclusas por default, por definição,
aqui a gente incluiu o iostream,
aqui estamos usando o cout e o endl.
Então eu precisei de maneira muito pensada os comandos que eu quero sem colocar toda a biblioteca,
não coloquei using namespace std, poderia se eu não quisesse ficar pensando os comandos,
a vantagem de pensá-los é que você define com maior precisão, não carregando bagagem extra para seu programa,
supondo que seu compilador não é experto o suficiente para filtrar o que você precisa,
e nem sempre vai ser.
Enfim, se você quiser certeza que só vai ser utilizado os comandos que você realmente precisa,
precise eles na definição aqui.
Aqui também estou incluindo list,
que é justamente o list da STL.
Também estou incluindo o algorithm,
permite trabalhar subcomponentes do list.
Na verdade permite trabalhar subcomponentes da STL.
Aqui estou incluindo nesse main,
isso aqui seria o main.cpp ou meuProjeto.cpp,
depende do ambiente que você está trabalhando,
estou incluindo naturalmente esse stdafx lá e cima.
Vejam que aqui eu tenho o main e depois eu tenho uma função.
Como o c está trabalhando com um dado cpp e você tem a main e depois
a construção de uma função que vai ser utilizada na main,
antes tem q colocar a assinatura da função,
isso imagino que já seja sabido por vocês.
Muito que obviamente vocês estão vendo
aqui eu estou usando um exemplo à lá procedimental
para exemplificar esses comandinhos ai,
aliás para exemplificar esses usos da lista.
Por quê? Porque é um exemplo muito pontual, não é isso?
De qualquer forma é sabido que o c++ trabalha tanto com procedimental quanto com orientado a objetos.
ele é híbrido quanto aos paradigmas.
Ainda que eu esteja trabalhando com procedimental,
veja que interessante,
aqui eu tenho uma função imprimeLista, mas ela não é uma função ordinária,
no sentido de comum, ela é uma função extraordinária
pelo fato de ser uma função template.
Veja que ela recebe um parâmetro listaRef, inclusive por referência,
que é constante e é um list parametrizado com algo.
Então uma função pode ser template, como vocês estão vendo aqui,
no sentido que o tipo de parâmetro que ela recebe pode ser parametrizado
com outro tipo ainda, com outra coisa ainda, aqui no caso é o T.
Depois vocês vão perceber que lá na chamada imprimeLista
eu passo algo, valores.
Quem que é valores?
Direto na função main aqui, valores é um list de inteiros,
é um list parametrizado com inteiro.
Então nessa chama aqui a função vai entender que o T vai ser um T para inteiros.
Bom, aqui eu criei então uma listinha chamada de valores,
poderia chamar de listaDeValores se eu quisesse um nome ainda mais pedagógico, mas está bem entendível, né?
Valores que é um lista de inteiros.
Então aqui estou usando ponteiros de objetos,
estou colocando supostamente objetos do tipo inteiro,
ou constante do tipo inteiro.
Aqui, por exemplo, eu estou colocando esses números constantes,
constante por assim dizer, os valores.
1 e 2 no front, no início da lista.
Então está ali, push_front(1) e push_front(2),
comandinho que vocês já conheciam, se não me equivoco, de aulas anteriores,
se não conheciam, estão conhecendo agora,
ele permite colocar no início da listas esses valores postos.
Aqui eu permito colocar no final da lista.
Então o list trabalha com push_front ou com push_back.
push_back vocês já viram em aulas passadas.
Então esses comandos permitem alimentar elementos inteiros dentro da lista de inteiros.
Se você for um pouco para frente, você vai ver o resultado.
Porque aqui eu coloquei os valores
e logo em seguida eu usei a função template para imprimir os valores.
Então está ali o resultado.
Foi inserido 1 no início, depois 2 no início,
depois foi inserido 4 no final e 3 no final.
Então está certo, 4 e 3.
Como é que consegui imprimir mesmo?
Porque eu chamei aqui o imprimeLista.
Values, que na verdade a gente chama de valores,
os valores contém o valor tal.
Esse valor tal vem justamente, insisto, da chamada da função imprimeLista,
função essa que é template, que recebe valores.
Quem que é valores mesmo?
Uma lista de inteiros, conforme vocês estão vendo aqui, recebeu esses valores.
Se eu olha lá, então, a função vai entender
que o parâmetro veio com inteiro, ela está tratando de uma lista de inteiros, essa listaRef.
Aqui eu testo se a lista não está vazia, aliás, eu testo se a lista está vazia.
Se ela está vazia, eu imprimo "Lista está vazia",
se não, eu venho aqui e faço algo, esse algo é interessante.
Eu poderia navegar na lista usando, por exemplo, um iterator,
e fazer o iterador apontar para valores.begin
e daí entra em um laço de repetição,
enquanto o iterador for diferente de lista.end,
eu vou percorrendo a lista fazendo iterador++.
Vocês já viram iteradores em outros momentos em nossas aulas,
até na aula passada de strings a gente usava o iterador fazendo isso.
Lembrando que você pode enxergar a string como uma "lista de inteiros".
Um pouquinho diferente disso, mas vamos supor que seja isso.
Lá na aula passada a gente viu essa história de iteradores,
lembramos que você pode fazer um iterador para navegar em uma dada coleção,
e aí você faz o iterador apontar para o início da coleção,
aqui seria valores.begin, daí você abria um laço de repetição,
enquanto o iterador for diferente de, por exemplo, valores.end,
eu vou navegando a lista através do iterador++.
Mas tem maneiras mais automatizadas de você fazer isso.
Você pode fazer assim, você pode criar um objeto, que aqui eu chamei de saída,
que é do tipo ostream_iterator.
Ou seja, ela é um iterador com fluxo de saída.
Iterador para o que?
Para o T, o T no caso em questão o inteiro, por causa do parâmetro que foi recebido, o inteiro.
Qual a ostream que ele vai usar?
Está aqui para parâmetro, é o cout que é um objeto do tipo ostream.
Então eu criei esse objeto aqui que vai permitir iterar de lista de inteiros,
iterar e imprimir.
Imprimir onde? Imprimir o cout.
Podia ser no arquivo de fluxo, por exemplo, mas aqui é no cout.
E cada vez que ele vai imprimir os elementos ele vai dar espaço entre os elementos.
Agora eu estou usando um algoritmo, um comando pronto, daquele algorithm,
que eu incluí no stdafx,
que permite, lá no objeto de saída, ele navegar e imprimir,
navegar e imprimir desde o início até o final da listaRef que na chamada em questão é o valores.
Então isso imprime automaticamente as coisas.
Só para mostrar que tem outras riquezas
algorítmicas, inclusive, no tocande da Standard Template Library do c++.
Bom, legal.
Aqui eu inseri conteúdos em valores.
Como que ele armazena internamente na lista?
Teria que ver a implementação.
Será que ele armazena só como valores? Será que ele cria, internamente, objetos inteiros para armazenar cada valores?
Boa questão para ser pesquisada.
O fato é que ele armazena, armazena os elementos,
posso imprimir os elementos utilizando toda essa estratagema aqui,
depois ainda posso ir na lista e fazer um sort.
O que que o sort vai fazer?
Ele vai organizar a lista do menor para o maior.
Está ali.
Depois do sort, ele deixou 1, 2, 3 e 4, ele organizou.
O sort deixa em ordem, ele ordena.
Como que ele consegue ordenar?
Qualquer coleção da STL que suporta o algoritmo sort,
ela consegue ordenar um conjunto de valores
se o operador de maior, se eu não me equivoco,
se não for de maior é o de menor, mas estou mais certo que seja o operador de maior,
se o operador de maior está sobrecarregado.
Então eu tenho uma lista de Pessoas, Pessoas tem operador de maior sobrecarregado,
por sorte vai funcionar.
Tenho quase certeza que é o de maior, se não for o de maior, é o de menor,
mas isso eu tenho explicado na minha página em algum momento.
Muito bem.
Isso posto, o sort permite utilizar esse operador, supostamente de maior,
para fazer a ordenação.
Ah professor, então se eu tenho uma coleção de coisas e quero que o sort funcione,
tenho uma lista de coisas, e quero que o sort funcione essas coisas tem que ter o operador de maior sobrecarregado, é isso em suma.
Agora preste atenção,
se for uma lista de endereços, de apontamentos,
ele vai ordenar pelos endereços de memória, bem entendido, essa que é a prioridade.
Mas enfim, existe o sort para ordenar.
Aqui eu estou reimprimindo a coisa reordenada.
Está aqui o resultado funcionando.
Avancemos.
Aqui eu estou com stafx.h de volta, que já apresentei.
Geralmente você cria um projeto no Visual Studio C++,
além de criar um .h, ele cria o respectivo .cpp.
Por quê? Porque é padrão, sempre que cria um .h cria um .cpp.
Salvo, obviamente, se você está criando o seu template,
quando você está definindo o seu próprio template, aí você faz tudo no seu .h, por motivos já explicados.
Aqui não é o caso, a gente está só utilizando templates prontos,
e aqui inclusive que está em tela, é um arquivo normal
.h que tem seu respectivo .cpp.
Geralmente ele fica vazio,
mas claro, imagine que tenho uma função que eu quero usar de maneira global no meu sistema,
quero ter um recuperaDataUniverso,
eu posso ter aqui então a assinatura da função, e posso ter a implementação aqui,
e aí eu consigo utilizar de maneira global no meu sistema.
Muito bem, só a título de exemplo, bem entendido.
Mas avancemos, continuo usando list, continuo usando algorithm.
Aqui no algorithm que tem aquele algoritmo copy que eu usei na função impressão.
Está aí, a função imprimeLista, voltando os slides.
Avancemos onde estávamos, no slide 6,
e vamos estudar esse exemplo aqui.
Esse exemplo que está aqui é uma evolução do exemplo estudado agora agora.
Continuo tendo a assinatura da função template,
que está aqui e vai aparecer depois no main,
supondo, obviamente, que esses dois slides aqui
estão em um único .cpp, main.cpp, meuProjeto.cpp, bem entendido.
Muito bem.
Então, está aqui a assinatura da função,
porque a função aparece depois do main.
Professor e seu eu colocasse a função, tirasse esse ponto e vírgula e já implementasse aqui?
Aí já estava resolvido.
É que aqui eu resolvi postergar a implementação da função para depois.
por isso que sou obrigado a colocar assinatura,
para o main saber que ela vai existir depois.
Muito bem, isso considerado,
esse exemplo aqui é uma melhora do exemplo anterior.
Então daqui onde estou apontando o mouse até aqui,
é a mesma coisa que já tinha antes, um detalhe ou outro,
por exemplo aqui o detalhe,
eu peguei a constante inteira SIZE, tamanho em inglês, que recebe 4.
Aqui eu criei um vetor de 4 tamanhos
e armazeno esses valores no vetor, 2, 6, 4 e 8.
Aqui é aquela rara situação onde eu posso usar o operador de recebe em vetores.
Eu acabei de criar o vetor e já estou dizendo que ele aponta para esse elemento aqui.
Mas enfim, ou ele é esse elemento aqui no final das contas.
Aqui eu estou criando agora um list.
Ali é vetorzinho normal né, ordinário por assim dizer,
aqui eu tenho um list.
Vocês estão vendo que eu estou usando aqui o operador de list dois pontos dois pontos std (std::list),
mas eu não preciso colocar esse std, porque se eu olha lá no meu...
Ah, precisa, porque eu não coloquei aqui o using namespace std.
Se eu colocar aqui, perdão,
vou voltar aqui um pouquinho.
Se eu colocar aqui using namespace std,
vou dizer que provavelmente eu posso fazer assim,
ai aqui eu não preciso contar que o list está dentro do std,
porque eu já estou colocando o using que está vindo a partir de lá.
Muito bem.
Isso aqui é só para uma explicação suplementar.
Também é sempre interessante.
Legal.
Então criei valores e criei outrosValores do tipo list inteiro.
Aqui estou colocando push_front,
push_back em valores, coisa que a gente já viu no exemplo passado.
Daí você tem aqui o imprimeLista,
que está usando aquela função que a gente já discutiu também, imprimindo os valores.
Faço o sort e reimprimo, que é o exemplo que a gente acabou de ver.
Agora uma segunda versão aqui.
Em outrosValores, eu vou usar um comando que vocês não viram ainda que é o insert.
Insert permite a partir do início, begin, de outrosValores,
colocar o que está no endereço de memória "a" até a "a+SIZE".
Quem que é "a" até a "a+SIZE"?
Na prática o "a" é o vetor,
o nome de um vetor é um ponteiro para o início dele.
Então ele vai colocar o conteúdo que está aqui até o final,
"a" de "a" até a "a+SIZE",
dentro de outrosValores, que é esse camarada aqui que é um lis.
Aqui eu vou imprimir para mostrar que funcionou.
Inclusive se eu for um pouco para frente,
você percebe que está aqui, outrosValores tem 2, 6, 4, e 8,
que é justamente os valores que eu recuperei dentro do vetor
e joguei dentro de outrosValores a partir do início dele.
Então no insert eu posso definir de qual posição eu quero inserir as coisas.
Tá, imprimi aqui.
Aqui eu estou fazendo um splice.
O splice vai permitir fazer o que?
É uma emenda.
Ele vai permitir em valores, a partir do final de valores,
emendar ou adicionar tudo que tem outrosValores.
só que nessa emenda, ele arranca as coisas que estão dentro de outrosValores e joga dentro de valores,
deixando outrosValores vazio.
Vamos ver lá, está aqui.
De fato ele colocou tudo em valores,
e deixou outrosValores vazio depois desse splice, dessa emenda.
OK.
Aqui também eu posso usar um sort,
se eu quiser para ordenar, ou ordenar aquele conteúdo que a gente já tem.
Aqui em outrosValores eu vou reinserir,
porque ele ficou vazio depois que eu fiz o splice,
os conteúdos do vetorzinho de volta, e fazer um sort nele,
e também vou ver como fazer depois esses sorts aí.
Está aqui, o sort na lista de valores,
depois em outrosValores eu coloquei de volta os conteúdos que estavam no vetor e fiz um sort.
Como lá já estava ordenado, se não me engano, no vetor, ele não altera em nada aqui.
Ah não, não estava ordenado, ele teve que ordenar esses dois aqui.
Então ele fez sim uma ação.
Legal.
Depois de ordenar as coisas, outrosValores,
fiz o sort em valores, inseri em outrosValores conforme já disse.
Fiz o sort, agora estou fazendo um merge.
O merge é uma fusão.
O merge tem um efeito muito parecido com o splice,
só que é um comando mais alto nível,
ele é simplificado, ele faz o merge e tudo, não preciso contar que é do início,
perdão, que é a partir do final,
ele já faz isso automaticamente,
e também vai deixar outrosValores vazio.
Se você for lá, você vai ver o resultado depois do merge.
Bom, aqui ainda, pessoal, eu tenho o pop_front e pop_back.
pop_front permite eu apagar o elemento do início,
e o pop_back permite apagar, retirar, o elemento do final.
Esse pop_back tem todos os contêineres sequenciais,
ou seja, a maior parte dos elementos da STL,
pop_front nem sempre, depende do elemento,
mesma coisa o push_front, por exemplo,
vector vai ter push_back e vai ter pop_back,
mas o vector não vai ter push_front e pop_front, coisa que o list tem.
O unique tira os elementos repetidos.
O swap troca elementos conforme a gente já viu em algum momento, se a gente não viu, estamos vendo agora.
O swap troca elementos entre duas listas, troca realmente, inverte essas duas listas.
O assign permite colocar valores, uma espécie de insert,
aqui por exemplo, colocar valores em uma outra lista,
de uma dada posição até uma dada posição, de uma posição inicial até uma posição final, por exemplo.
Aqui o merge de volta.
E aqui o remove, estou removendo algo da posição 4.
Isso aqui, então, são só comandos para vocês verem,
que além daqueles que a gente um pouco anteriormente de vector,
list tem mais uma série de elementos.
Aqui o list é diferente da lista que gente criou.
A lista que a gente criou template, a nossa lista,
ela foi feita por nós, a gente teve que desenvolvê-la.
Aqui o comando list é um comando que está pronto, é um comando da STL que a gente vem e aplica.
Bom, daí depende do custo-benefício.
Se eu quero uma lista para economizar e processamento para um dado contexto,
pode ser que seja melhor eu ter minha própria lista, eu mesmo desenvolver.
Se eu estou numa situação onde não estou me preocupando no momento com memória,
com tempo de processamento, que eu imagino que é a maior parte dos casos,
não sei se corretamente ou incorretamente, depende do contexto,
mas o fato é que você pode utilizar lista tranquilamente.
Ora, qual um contra ponto do list?
Vocês estão vendo que tem uma série de comandos, nem sempre você vai usar todos esses comandos.
Então tem uma bagagem que nem sempre você vai utilizar
e essa bagagem interfere no tempo de processamento,
em termos de memória, bem entendido,
Mas enfim,
em escala, conforme a situação, pode ser que isso não seja tão perceptível,
computador de grande porte, até para os pcs normais que a gente usa, seria irrelevante.
Mas claro que também, como diz o ditado, de grão em grão a galinha enche o papo.
Mas enfim, discussão posta.
Fato é que a gente tem esse list e ele está pronto
e tem uma série de recursos disponíveis,
que tem a vantagem de permitir a gente fazer uma série de manobras,
e ao mesmo tempo, se a gente não for utilizar todos os recursos, você está tendo alí um...
uma sobrecarga, que não necessariamente você precisaria ter se você não vai utilizar esses recursos.
Tudo bem.
Está aqui o imprimeLista que é a função template que a gente já discutiu.
E está aqui a discussão do algoritmo.
Legal, agora a gente passa para a classe vector.