Containers e iterators
Teoria
E aí galera, aqui é o Juan do Responde Aí, e hoje vamos continuar nossos estudos de C++ aprendendo sobre containers e iterators
A Standard Template Library – STL.
Os templates, uma forma especial de preparar funções e classes para receber tipos de dados variados.
Como você pode imaginar, eles podem ser muito úteis na hora de criar bibliotecas versáteis e cheias de funções legais.
E foi exatamente isso que os criadores do C++ fizeram, eles criaram a Standard Template Library – STL, um conjunto gigantesco de templates de classes que contém estruturas de dados muito usados e funções importantes, organizada em forma de algoritmos, funções, containers e iterators.
A STL facilita muito a vida dos programadores, mas devido ao tamanho massivo dessa biblioteca, hoje vamos dar atenção a dois pontos em especial: o container e os iterators.
Os containers
Pra começar, vou te dizer o que é um container (alguns professores traduzem para contêiner): é uma estrutura que consegue armazenar dados de maneira organizada, como uma lista, pilha, conjunto, dicionário, etc.
É como se o container fosse um objeto que consegue armazenar outros objetos.
Você pode pensar nos containers como uma evolução das arrays. Os containers são programados através de templates de classes, e por isso já vêm de fábrica com vários métodos legais que os tornam muito mais poderosos do que as arrays que já estamos acostumados.
O container
O container é um template de classe que existe na STL, e que se comportam como as arrays comuns, guardando dados na memória de forma sequencial. Show de bola. Mas e daí?
Qual é a vantagem de usar esse tal de ao invés das arrays que já estamos acostumados?
A principal vantagem é que os são alocados dinamicamente por padrão! Isso quer dizer que a gente pode mudar o tamanho de um vector a vontade, sem ter que nos preocupar em fazer alocação dinâmica de memória. A desvantagem é que os consomem mais memória do que as arrays comuns, porém é o preço a ser pago pela facilidade de uso.
Então, sem mais enrolação, vamos lá usar um vector:
Para adicionar um ao nosso programa precisamos incluir o cabeçalho :
Agora, você deve pensar no como um template de classe (afinal ele é isso mesmo), então quando você for criar um vetor, basta você instanciar um objeto da classe inserindo o tipo entre as chaves:
E assim você criou um completamente vazio. Para saber o tamanho da array que a gente criou nós podemos usar o método :
Para inserir um elemento em um devemos usar o método , que adiciona um elemento no fim do vector:
Para visualizar os elementos de um vetor podemos usar os colchetes , que nem nas arrays, para qualquer elemento contido no vetor. Mas também podemos usar o método para ver o primeiro elemento e o método para ver o último elemento.
A qualquer momento podemos remover um elemento do nosso . Usando o método a gente remove o último elemento do
E se for necessário, podemos apagar todos os elementos do com o método
Viu só que legal?! Os são que nem as arrays com esteroides!
Outras formas de instanciar um
Na parte anterior a gente criou um e preencheu ele usando o várias vezes seguidas, e você deve ter notado que essa é uma maneira um pouco cansativa de fazer. Imagina, preencher 15 valores dessa forma...
Mas por sorte, a classe possui um construtor sobrecarregado, e existem várias maneiras de preencher um no momento em que ele é instanciado:
Na linha 02 criamos um totalmente vazio, como fizemos anteriormente. Na linha 03 criamos um com 5 espaços na memória, mas não definimos os valores que estão armazenados. Essa não é uma boa prática, pois os valores inicialmente contidos em vão variar de acordo com cada compilador. Na linha 04 criamos um com 7 espaços na memória, e todos eles preenchidos com o caractere . E na linha 05 criamos um que é uma cópia exata de outro
Mas e se você quiser um preenchido com diversos valores? A melhor forma é transformar uma array em um . A gente faz isso inserindo os ponteiros para a primeira e para a última posição da array no construtor do :
É importante que a array e o sejam do mesmo tipo!
Outra vantagem é que criar uma partindo de uma array é que você não é obrigado a passar a array completa. Se quiser, pode passar um ponteiro para uma posição inicial qualquer, e um ponteiro para uma posição final que desejar.
Os iterators
Os iterators (alguns professores traduzem para iteradores) são parecidos com os ponteiros, mas o iterator ajuda a gente a trabalhar com os containers. A vantagem de usar iterators é que eles funcionam com a mesma lógica em todos os containers, o que nem sempre é verdade no caso de usar ponteiros.
Antes de mais nada, para usar um iterator precisamos ter um container em funcionamento, então vamos criar um a partir de uma array:
Os iterators e containers são intimamente relacionados, e por isso devem ser do mesmo tipo então pra declarar um iterator que aponta para um fazemos:
Todo container possui ao menos dois métodos especiais que trabalham com os iteradores, o que faz o iterator apontar pro início do container, e o para apontar pro espaço vazio após final do container.
Isso é especialmente útil quando vamos usar um loop em cima dos . Por exemplo:
Na linha 12 criamos um laço que o iterador começa a contar na posição inicial do e avança de um em um até a última posição final. Na linha 13 a gente imprime o valor apontado pelo iterator: da mesma forma que um ponteiro, usamos o operador para acessar o conteúdo.
Importante: lembre-se que o valor apontado por é o “vazio” após o final do container, então quando imprimimos esses valores, encontramos lixo de memória.
Um segundo tipo importante são os que nos ajudam andar ao contrário sobre um container. Temos o aponta para o último elemento, e o que aponta para o espaço vazio antes do primeiro elemento:
No caso dos iteradores reversos, o não faz parte do intervalo. E se você imprimir o valor apontado por ele, vai encontrar lixo de memória:
Erros comuns
- Tentar inicializar um usando como se fosse uma array:
- Tentar ampliar o tamanho do vector usando colchetes ao invés do :
- Usar o operador de decremento ao invés de com os iteradores reversos também é um erro comum. Mesmo que a gente esteja andando “de trás pra frente” com os iteradores reversos, nós devemos usar o operador
Dicas:
Na hora de preencher um vetor usando array precisamos saber o tamanho dessa array. Mas pode haver situações em que o tamanho da array é variável ou então é desconhecido.
Pra contornar isso, podemos usar uma expressão para calcular o tamanho da array automaticamente:
E vamos poder gerar um vector da seguinte forma:
Resumo do básico sobre o container e seus iterators básicos
E por hoje é isso pessoal. Vamos ver agora uns exercícios de fixação.
Outras formas de instanciar um v e c t o r
Os iterators
Erros comuns
Dicas:
Resumo do básico sobre o container v e c t o r e seus iterators básicos
Exercícios Resolvidos
Exercício Resolvido #1
Prof. Edson Moreno – PUCRS – Lista de exercícios STL
Escreva um programa explorando STL que declara um vetor de inteiros. Armazene no vetor os 5 valores informados e então imprima o vetor de elementos com o comando cout.
Passo 1
E aí, galerinha. Aqui é o Juan, e vamos pra mais um exercício de C++
A questão pede pra gente explorar a STL e criar um vetor, ou seja, ela quer que a gente use o template para criar um vetor. E depois quer que a gente preencha e imprima o vetor que a gente criar
Então, pra começar, a gente vai ter que incluir os cabeçalhos necessário e
Passo 2
Belezinha. Vamos pra onde vamos declarar o nosso vetor. Lembre-se que a questão pede que o vetor seja do tipo inteiro.
Os vetores são um template de classe, então para instanciar o vetor a gente usa a classe junto com o tipo entre as chaves , e depois o nome do vetor:
No código fazemos assim:
E assim a gente cria um vector completamente vazio.
Passo 3
Para preencher esse vetor vazio a gente vai usar o método que adiciona um elemento no fim do vector:
O vetor está completamente vazio. Então quando a gente usar o método o valor vai ser adicionado na primeira posição do vetor (de índice 0). No código fica:
E usamos o método novamente para adicionar mais quatro valores no nosso vetor. A gente pode adicionar qualquer valor inteiro nesse vector.
Passo 4
E estamos quase acabando!
Só falta gente imprimir o conteúdo do vector. Pra isso a gente vai usar o e imprimir cada uma das posições do vetor.
Para imprimir a primeira posição a gente faz assim:
E agora a gente imprime as próximas posições do vetor:
E quando a gente rodar o código teremos a seguinte saída:
E prontinho pessoal. Terminamos a questão!
Resposta
Exercício Resolvido #2
Adaptado de Universidade Federal do Triângulo Mineiro – Programação Computacional – Lista de Exercícios Nº9
Crie um programa que:
- Crie um de 5 números inteiros.
- Peça para o usuário digitar os 5 números e insira-os no vector.
- Mostre na tela os 5 números
- Apague tudo que estiver contido no
Passo 1
Vamos lá, galera
Essa questão vai ser legal pra gente exercitar o uso do container . Então, já vamos começar incluindo o no cabeçalho do programa.
Passo 2
O passo seguinte é criar um vector dentro da nossa . A questão pede que o vector seja criado com 5 espaços iniciais. Então vamos usar a seguinte sintaxe:
A questão pede que o vector seja do tipo inteiro, e que o tamanho seja 5. Então vamos criar um vector chamado com essas especificações:
E prontinho. O mais difícil já passou.
Agora, a gente pode usar o como os vetores normais que já estamos acostumados.
Passo 3
Para preencher um vector com dados do usuário, é só a gente criar um loop, que nem a gente fazia com as arrays.
Então vamos criar um loop que dê 5 voltas. Em cada volta vamos usar o para pedir pro usuário digitar um valor, e o para guardar o valor digitado na posição correspondente dentro do vector:
Passo 4
A próxima etapa é imprimir o conteúdo do vector.
E aqui não tem nenhum segredo. É só a gente usar um loop pra imprimir cada uma das posições do vector.
Então a gente cria um loop com cinco iterações, e pra cada iteração o vai imprimir a posição correspondente do vector.
No código acima eu adicionei o caractere especial pra dar um espaço na hora de imprimir o conteúdo do vector.
Passo 5
E agora vamos pra última etapa.
A questão pede pra gente apagar o conteúdo do vector. Pra isso, é só a gente chamar o método , e todo o conteúdo do vector será apagado.
E prontinho. Questão concluída!
Resposta
Exercício Resolvido #3
Adaptado de Instituto Federal de Goiás – Atividade sobre Ponteiros
Implemente um programa em que obtenha do teclado o tamanho (inteiro) de um vetor, após isso crie um vetor de com o tamanho obtido e então leia do teclado a os números digitados pelo usuário e armazene no novo . Exiba os valores contidos vetor. Use iteradores nos loops.
Passo 1
Vamos lá, pessoal!
A questão pede pra gente criar um vetor com tamanho determinado pelo usuário, e depois preencha esse vetor. A questão traz um detalhe, ela quer que a gente use iterators nos loops. Mas você vai ver que é tranquilo.
Mas antes de começar, vamos iniciar o cabeçalho do código.
Passo 2
Antes de criar o vector que vamos trabalhar precisamos saber qual será o tamanho dele. Então vamos criar uma variável inteira e pedir que o usuário nos diga qual o tamanho que ele deseja pro vector.
Passo 3
Show de bola. Agora temos tudo que precisamos pra criar nosso vetor. Pra criar um vetor com um tamanho determinado a gente faz assim:
O tipo que precisamos é o e o tamanho que desejamos está contido na variável tamanho. No código fica assim:
Passo 4
Um detalhe importante nessa questão é que ela pede que a gente use um iterator nos loops. Então vamos declarar um iterator:
O template que a gente está usando é o , o tipo do vector que criamos é o . Ou seja, a gente vai criar um iterator para vectors do tipo float. Vamos chamar de
Não se esqueça que um iterator é um tipo especial de ponteiro.
Passo 5
Com tudo no ponto a gente pode preencher o nosso vector com valores inseridos pelo usuário. Pra isso a gente vai utilizar um loop que vai começar do início do vector e vai até a última posição do vector.
Para isso, a variável de iteração do loop vai ser o iterador , que vai ser iniciado pelo método , e terá como ponto de parada o método . O incremento vai acontecer de um em um:
O iterator é um tipo especial de ponteiro. Ou seja, a cada iteração o tem acesso a um endereço de memória do vector. Então, podemos pedir pro usuário inserir valores e podemos guardar esses valores no endereço apontado por :
Passo 6
Prontinho. O vector está preenchido. Agora vamos exibir os valores contidos nesse vector. Pra isso vamos fazer um loop muito semelhante ao anterior: O começa em e avança de um em um até
A cada iteração o terá acesso a um endereço de memória do vector. Nosso objetivo é apenas imprimir cada elemento do vector, então vamos apenas imprimir o conteúdo apontado por :
E pronto! Podemos finalizar o programa:
Resposta
Exercício Resolvido #4
UFU – FACOM – Lista de exercícios de programação em linguagem C
Faça um programa que preencha um vetor com 10 números reais, calcule e mostre a quantidade de números negativos e a soma dos números positivos desse vetor.
Passo 1
E ai, lets vamo!
Para essa questão nós vamos utilizar os vectors, então já vamos começar incluindo os cabeçalhos necessários:
Seguindo em frente, vamos pra função , onde vamos declarar as variáveis que vamos precisar.
Vamos precisar de um vector com 10 espaços que armazene os valores inseridos pelo usuário. Uma boa dica é sempre declarar um iterator junto com um vector, então vamos declarar um iterator também.
Para declarar um vector com tamanho definido a gente faz assim:
E para declarar um iterator nós usamos o seguinte comando:
A questão pede que o vector receba números reais, então o tipo deve ser .
O objetivo da questão é que a contabilize a quantidade de números negativos foram inseridos no vetor, e que some todos os números positivos inseridos no vetor. Então vamos criar duas variáveis que sejam capazes de armazenar esses valores:
Passo 2
O próximo passo é pedir pro usuário inserir os valores no vector. Vamos precisar de um laço for que que inicie no começo do vector e aumente de um em um até o final do vector. Pra isso podemos utilizar o iterator e os métodos e para determinar o ponto de início e fim do loop:
Dentro do loop vamos usar o para pedir que o usuário insira um valor. E com o vamos guardar o valor inserido dentro do vector. Lembre-se que um iterator é, na verdade, um ponteiro que aponta pras posições do vector. Então, podemos usar o iterator para inserir o valor digitado no local de memória apropriado.
E não acaba por aqui. Nós podemos aproveitar que já estamos dentro de um loop e contabilizar a quantidade de negativos e somar os valores positivos!
Pra isso vamos precisar de uma estrutura que testa se o número é negativo ou positivo. Se for negativo, então a variável aumenta mais um. Se for positivo, então a gente soma o valor inserido com os demais dentro da variável , neste caso, lembre-se que podemos usar o iterator para acessar o local de memória em que o valor está armazenado.
Em resumo, a cada iteração do loop, o valor inserido pelo usuário é armazenado em um endereço de memória do vector , linhas 15 a 17. Em seguida, se o número for negativo a contagem de negativos aumenta, linhas 18 e 19. Se o número for positivo, somamos ele com os demais positivos, linhas 20 e 21. Em seguida, o loop é finalizado
Passo 3
Por fim, pra terminar a questão, é só a gente imprimir pro usuário a quantidade de números negativos, e a soma dos números positivos:
E finalizamos o programa!
Resposta
Exercício Resolvido #5
UFU – FACOM – Lista de exercícios de programação em linguagem C
Faça um programa que receba do usuário um vetor com 10 posições. Em seguida, deverá ser impresso o maior e o menor elemento do vetor.
Passo 1
Fala aí, tudo tranquilo?
A questão pede pra gente criar um vetor de 10 espaços e depois determinar o maior e menor valor.
Pra isso vamos precisar de uma vector para armazenar os valores, e de um iterator para executarmos loops sobre esse vector. Também vamos precisar de duas variáveis que armazenem o maior e o menor valor.
Então vamos começar com o cabeçalho do código.
E em seguida vamos criar as variáveis. Um vector do tipo double chamado . Um iterator para vector do tipo double chamado . E duas variáveis do tipo double e :
Passo 2
Agora precisamos que o usuário insira os valores nesse vector. Para isso, vamos usar um laço com o iterator que vai começar em , vai acabar em e vai avançar de um em um.
E dentro desse loop vamos usar o iterator , que pra cada iteração vai assumir o endereço de memória de uma posição desse vector. Então vamos usar o operador para acessar o local de memória apontado por e armazenar um valor:
Passo 3
O passo seguinte é definir qual é o menor e o maior valor presente dentro do vector.
Antes de mais nada, precisamos inicializar o valor das variáveis e . Então vamos iniciar as duas variáveis com o primeiro valor do vector
Em seguida, vamos criar um laço que a cada iteração teste os valores presentes no vector.
O primeiro teste vai conferir se o valor atual de é maior do que o contido na variável . Caso seja, então o valor da variável deve ser atualizado
E depois fazemos um segundo teste, em que conferimos se o valor atual de é menor do que o valor contido na variável . Se for, então a variável deve ser atualizada.
E o loop está finalizado.
Passo 4
Agora só resta informar ao usuário qual é o maior e o menor valor.
Pra isso, a gente simplesmente vai utilizar o para imprimir o valor contido nas variáveis e
E com isso finalizamos a questão.
Resposta
Resposta
Ei, a resposta está no passo a passo :)
Exercício Resolvido #6
Fonte: Elaboração própria
Receba N valores do usuário. Armazene os valores positivos em um vector e os valores negativos em outro vector. Exiba os vectors pro usuário.
Passo 1
Vamos nessa, pessoal!
A questão está pedindo pra gente criar dois vectors e preencher um deles com valores positivos e outro com valores negativos.
O detalhe é que esses valores serão inseridos aleatoriamente pelo usuário. Então, vamos usar um loop para receber todos os valores e uma estrutura para separar os valores positivos e negativos.
Mas vamos começar escrevendo o cabeçalho da questão, e adicionamos o
Passo 2
Então vamos começar o código dentro da
Para declarar um vector vazio a gente faz assim:
Vamos precisar declarar dois vectors, um para armazenar valores positivos e outro para armazenar valores negativos. Os valores inseridos pelo usuário podem ser qualquer valor real, então esses vectors devem ser do tipo .
Para realizar loop sobre esses vectors vamos precisar e um iterador. Para declarar um iterador a gente faz assim:
Mas antes de usar os vectors nós vamos precisar receber os valores de usuário. Para isso, vamos criar uma variável do tipo que vai receber os valores do usuário. E uma variável inteira onde o usuário vai informar quantos valores ele pretende inserir.
Show. Isso termina as declarações.
Passo 3
Agora vamos preencher os vetores. Primeiro de tudo, precisamos saber quantos valores serão inseridos. Então pedimos essa informação pro usuário e armazenamos na variável
Agora vamos preencher!
Vamos iniciar um loop que vai de até antes de de um em um. A cada iteração vamos receber um valor do usuário e armazenar na variável . Em seguida, vamos utilizar uma estrutura para verificar se o valor é positivo ou negativo.
Na hora de preencher os vectors nós vamos utilizar o método que vai inserir o valor na última posição do vector.
E prontinho! Vectors preenchidos.
Passo 4
Agora vamos imprimir o conteúdo de cada um dos vectors.
Novamente, vamos utilizar um loop , mas dessa vez vamos usar um iterador. A gente inicializa o iterator utilizando o método e o loop vai crescer de um em um até atingir a posição final do vector, que é dada pelo método .
O iterator é um ponteiro para o vector, então, na hora de exibir o conteúdo, é só a gente tratar o iterador como ponteiro e usar o operador para acessar o conteúdo.
Primeiro fazemos esse processo para o vector
E depois fazemos o mesmo para o vector
E aí é só rodar o código e ver o resultado.
E prontinho!
Resposta
Exercício Resolvido #7
Fonte: Elaboração própria
Receba N valores. Imprima os valores inseridos de trás pra frente
Passo 1
E aí galera, preparados?!
A questão é bem direta. Ela pede pra gente criar um vector preenchido com valores do usuário, e depois imprima os valores de trás pra frente.
Lembre que a melhor forma de andar de trás pra frente sobre um vector é usando um iterador reverso.
Mas antes, vamos adicionar o cabeçalho do código incluindo a biblioteca vector
Passo 2
Vamos iniciar a e declarar as variáveis necessárias.
A gente vai precisar de um vector! Pra criar um vector vazio a gente faz assim:
Então vamos declarar um vector chamado . Usaremos o tipo para que o usuário seja capaz de inserir números com alta precisão.
Para imprimir esse vector de trás pra frente vamos precisar de um iterator reverso.
Vamos criar o iterator reverso que deve ser do tipo , pois o tipo dos iterators deve ser compatível com o tipo dos vectors.
Mas antes de usar o vector, nós vamos precisar receber os valores de usuário. Para isso, vamos criar uma variável do tipo que vai receber os valores do usuário. E uma variável inteira onde o usuário vai informar quantos valores ele pretende inserir.
Show. Isso termina as declarações.
Passo 3
Agora vamos preencher os vetores. Primeiro de tudo, precisamos saber quantos valores serão inseridos. Então pedimos essa informação pro usuário e armazenamos na variável
Agora vamos preencher!
Vamos iniciar um loop que vai de até antes de de um em um. A cada iteração vamos receber um valor do usuário e armazenar na variável . Em seguida usamos o método para preencher o vector.
Passo 4
Agora vamos imprimir o conteúdo do vector em ordem inversa.
Novamente, vamos utilizar um loop , mas dessa vez vamos usar o iterator reverso. A gente inicializa o iterator reverso utilizando o método que aponta para o fim do vector, e o iterator reverso vai crescer de um em um até atingir a posição inicial do vector, que é dada pelo método .
O iterator é um ponteiro para o vector, então, na hora de exibir o conteúdo, é só a gente tratar o iterador como ponteiro e usar o operador para acessar o conteúdo.
E prontinho. Vamos rodar o código pra ver o resultado:
E é isso! Questão concluída.
Resposta
Exercício Resolvido #8
Fonte: Elaboração própria
Crie uma função que receba um vector de floats e o imprima na tela
Passo 1
Vamos lá pessoal
Essa é uma questão que pede pra gente criar uma função que recebe um template, nesse caso, o template vector.
Aqui não tem grilo, uma função que recebe um template é exatamente igual a uma função normal! Nós só temos que estar atentos a definir o tipo do template nos argumentos da função.
Então vamos começar a questão escrevendo o cabeçalho do código incluindo o container vector
Passo 2
Agora vamos criar uma função chamada que recebe como argumento um vector. Essa função será do tipo , pois não terá retorno.
A única diferença é que o argumento será do . Ou seja, termos que definir o tipo de vector que será recebido. Nesse caso, será do tipo .
Depois, dentro da função vamos usar um loop para imprimir todos os itens do vector
Passo 3
Agora vamos testar!
Vamos criar um vector preenchido com 4 itens
E depois executamos a função
Rodando o código vamos ter o seguinte resultado