Bom, a classe Vector
é também um componente da STL, standard template library,
uma classe predefinida.
Está aqui o nosso stadfx.
Então eu fiz o include do iostream,
estou precisando os comandos que vou usar no iostream,
e está aqui o include vector.
Aqui, provavelmente, faltou um using namespace std
ou aqui eu venho, se eu não quiser colocar o using namespace std lá,
eu tenho que vir aqui e colocar que esse vector é criado a partir da std.
Então tem essas duas maneiras de fazer.
Aqui eu vou deixar assim até para a gente criar um pouco de variação de contexto.
Está ali o stdafx.h.
Só voltando aqui,
vocês estão vendo que eu estou usando o vector,
não estou usando o algorithm, porque prometo não usar nesse exemplo dado.
Está aqui uma função template,
só que agora uma função template que trabalha com vector e não com list,
inclusive já está a implementação dela ali,
ela imprime vetor.
Eu poderia imprimir o vetor usando o algoritmo de copy,
tranquilamente, tal qual fiz no exemplo anterior,
só que aqui, em vez de usar o copy, eu resolvi usar o iterador mais na unha.
Então estou criando dentro da função um iterador, um const iterator
que trabalha com vector .
Bom, em função dessa função que estou explicando agora, me permitam fazer um ajuste aqui.
Eu vou desdizer tudo que disse antes e voltar a usar um vector aqui,
e vou lá colocar explicitamente o using namespace std.
Isso vai me evitar confusão.
Legal.
Modo exibição, voltamos para o que interessa.
Está aqui a função imprimeVetor,
recebe um vetor por referência que é um vector parametrizado com T.
Conforme o tipo que eu receber aqui que vai ser o tipo que vou trabalhar dentro dessa impressão, uma função template.
Vejam que legal, no c++ eu posso ter classe template e função template, como vocês estão vendo aí.
Muito bem.
Aqui eu criei um iterador que chamei de Iterador2, por algum motivo,
ele é um iterador constante, ou seja, ele permite iterar para ler, mas não para modificar,
para vector de T.
Se você olhar a primeira chamada de imprimir,
ele imprime o vetor.
Quem que é esse vetor aqui?
É um vector de inteiro, então aqui o T vai ser inteiro.
Olha que interessante, o const_Iterator é na verdade uma classe,
que permite definir objetos iterador que são objetos para iterar, "ponteiros de alto nível",
e veja que ele é definido de maneira aninhada dentro do vector.
Olha que interessante.
Então a classe vector, assim como a classe list,
vai ter dentro de si classes aninhadas, ou elementos aninhados, que são iteradores,
sejam constantes, sejam não constantes.
Interessante, né?
Só para lembrar que a gente estudou aninhados em slides anteriores.
Mas em fim, o fato é que estou definindo um iterador aqui, o Iterador2.
O iterador aponta para o início do vetor em questão,
então está aqui o vector,
enquanto não estiver no final dele, o iterador é incrementado.
E aqui, já que o iterador é um objeto
de uma classe que foi preparada para simular o comportamento de um ponteiro,
eu tenho esse operador sobrecarregado
que permite ir lá e recuperar o conteúdo apontado, por assim dizer, pelo Iterador2.
Então aqui eu estou imprimindo via iterador, na prática,
o conteúdo do vector que está vindo pelo parâmetro.
Insisto, criei o iterador, faço esse iterador apontar para o início do vector em questão,
enquanto o iterador não estiver no final do vector em questão, ele vai incrementando o iterador.
E aqui, através do asterisco, do operador asterisco,
eu consigo recuperar o conteúdo do elemento apontado pelo iterador.
Muito bem.
Isso considerado, está aqui a definição da função.
Por que ela está definida aqui? Porque a implementação veio depois.
Se ela já estivesse definida e implementada aqui,
ela obviamente não precisava definir, já ia estar pronta lá.
Mas tudo bem.
Aqui foi usado essa opção de criar a função depois da main,
se é uma função criada depois da main, antes tem que ter a assinatura da função,
como você está fazendo tudo em único .cpp, seja main.cpp ou meuProjeto.cpp da vida.
Legal.
Entrando agora na função main em si.
Eu criei uma constante TAMANHO 6,
criei um vetor "a" de TAMANHO, 6 obviamente, com esses elemento aqui,
inclusive já estão ordenados.
Conteúdo de array, array é um dos nomes que a gente dá para vetor também, em fim,
usando a noção de ponteiro.
Conteúdo do array "a", ou seja, do vetor "a",
usando a notação de ponteiro.
Olha que interessante:
eu posso navegar em um vetor usando o operador de colchetes,
crio lá um...
Será que eu faço aqui?
Posso fazer.
Cadê?
Está aqui.
Bom, se tem espaço...
Então vou fazer essa arte aqui.
Posso vir aqui e fazer o seguinte...
Isso aqui vocês sabem, só para fim de preciosismo mesmo.
Na verdade em um vetor você vai fazer o óbvio.
O que é o óbivio?
Vai criar um int "i" qualquer, que recebe 0,
no caso ali, enquanto "i" for menor que TAMANHO,
você faz um i++.
Isso aqui é programação pré-escolar, vamos dizer.
Você vem aqui e faz um a[i],
usando, então, o operador de colchetes do vetor normal.
Isso aqui é sabido desde sempre para quem é da computação, obrigatório inclusive.
Precisamos de espaço aqui, então vou colocar essa chave depois lá, embora não seja meu padrão de codificação,
mas nada obsta.
Está aqui o vetor normal, como sempre,
navegando o vetor via colchetes, em fim.
Tem outra maneira para navegar em um vetor que é via aritmética de ponteiro.
Eu posso criar um ponteiro de inteiro que aponta para "a", "a" é um ponteiro para o início do vetor.
Novamente, cria aqui um ptr, que é um ponteiro de inteiro, que aponta para "a",
"a" é o nome do vetor e também um ponteiro para seu início,
enquando o ponteiro não estiver em "a+TAMANHO",
ou seja, não estiver apontando para o final do vetor,
eu faço ponteiro++,
fazendo o ponteiro avançar via aritmética de ponteiro com um apontamento aqui.
E aí eu pego, e através do asterisco na frente do ponteiro
que trás o conteúdo do cara apontado, e imprimo o conteúdo.
Na primeira passagem vai imprimir o 1, depois vai imprimir o 2, depois o 3 e assim vai.
Usei aqui a aritmética de ponteiro.
Para quem não lembre de aritmética de ponteiro
você pode entrar no meu grupo de slides de programação 1,
olhar lá, está bem organizado no meu site,
e o tema aritmética de ponteiro tem toda uma explicação no slides.
Mas em fim.
Isso aqui só lembrando que tem essas duas maneiras de imprimir um vetor,
ou usa o operador de colchetes normal,
ou via aritmética de ponteiro.
Agora estou criando aqui um vector,
não mais um vetor normal,
um vetorzinho normal, estou criando um vector.
que chamei aqui de Vetor, vou chamar de vetorzão porque é um v maiúsculo.
Nesse vertozão aqui eu posso, depois de criado,
já que ele é um componente da STL,
eu posso ver o size e o capacity.
size e capacity.
Então tamanho e capacidade.
Tamanho é o tamanho que ele tem
e a capacidade é o quanto foi alocado para ele.
O tamanho diz o quanto foi utilizado,
e a capacidade o quanto foi alocado para esse vetor.
Naquele momento, tanto o tamanho inicial quanto a capacidade é zero,
aqui o objeto não alocou nada ainda.
O vector, muito provavelmente trabalhe dentro de si com alocação e desalocação de memória gente.
Ele aloca, então, um vetor do tipo dado,
até essa memória encher,
depois quando ela enche, ele vai lá e aloca mais,
e assim ele vai usando o algoritmo de lógica
para definir o quanto ele vai alocar, não é isso?
Usando alocações de memória internamente.
Aqui eu inseri dentro do vector usando push_back, vector não tem push_front, só tem push_back,
inclusive, provavelmente, por causa dessa política de alocação e desalocação, mas em fim,
ele está inserindo esses números aqui.
E aí eu vou ver se mudou o size e capacity.
Mudou o size, por enquanto o size, o tamanho, virou 3,
e o capacity continua 3.
então ele alocou o que ele está precisando no momento, mas nem sempre é assim, ele pode alocar mais,
se ele percebe que tem um determinado crescimento em um dado momento.
Vou usar a impressão do vetor,
usando a função template.
Aqui essa função template, insisto,
como não usei o algoritmo copy, poderia, mas não usei,
o copy também funcionaria com o vector tal qual funcionou com o list,
usando aquelas estratégias utilizadas, bem entendido,
aqui eu estou utilizando um iterador mais na unha.
Bom.
Se a função imprimeVetor usa um iterador normal,
aqui numa função main, eu estou criando uma outra listagem.
Essa outra listagem que está aqui, que estou sublinhando com o mouse,
ela poderia estar em outra função, mas não está, está dentro da função main.
Aqui estou criando um reverse_iterator,
que também pertence a vector.
Você tem esse iterador, esse iterador aponta para o início reverso, ou seja, o fim,
enquanto o iterador não estiver no fim reverso, ou seja, no início,
ele vai incrementando no sentido contrário,
e aí eu vou imprimir meu valor aqui,
Então esse reverse_iterator permite navegar ao contrário no vetor.
do fim para o início, conforme vocês estão vendo aqui.
O vetor com iterador normal fez 2, 3, 4,
com o reverso fez 4, 3, 2, naturalmente.
Legal.
Aqui nós estamos num segundo exemplo com vector.
Vocês estão vendo que agora estou usando o vector e estou usando o algorithm também.
Agora significa que vou fazer alguma arte com algorithm, coisa que eu não estava fazendo antes.
Bom, está aqui o exemplo.
Esse exemplo aproveita parte do exemplo anterior
e expande um pouco mais.
Aqui a gente criou a constante TAMANHO, criou um vetor normal,
criei um vector que vou chamar de vetorzão aqui,
inseri nesse vector o conteúdo do vetorzinho,
via apontamentos aqui,
ele vai recuperar tudo que tem no endereço de memória,
através da navegação de memória, ele vai conseguir inserir todas as coisas lá dentro,
obviamente que ele está fazendo isso no âmbito da construtora do vector,
muito embora a gente não esteja enxergando isso, a gente intui isso porque a gente conhece orientação a objetos.
Aqui eu estou criando o objeto Saida,
que é um ostream_iterator que trabalha com cout para inteiros,
e aqui eu estou usando o copy,
para imprimir em Saida o conteúdo do vetorzão do início até o final.
Eu posso, se eu quiser, também usar outros comandos,
como por exemplo, o primeiro elemento, o último elemento, através do front e do back.
Aqui eu estou usando no vetorzão, que é um vector, o operador de colchetes,
você sabe que vector permite trabalhar com operador de colchetes.
Por quê?
Porque o vector foi preparado para trabalhar com o operador de colchetes,
tal qual a classe string também foi preparada para trabalhar com o operador de colchetes.
Então aqui na posição 0 do vector,
ou seja, na posição 0 do vetorzinho que é a essência do vector,
eu estou colocando o valor 7,
e aí a hora que eu imprimir
não vai sair mais 1, 2, 3, 4, 5, 6,
vai sair 7, 2, 3, 4, 5, 6,
obviamente,
e aqui eu estou usando o comando copy para fazer isso.
Está ali inclusive o que eu falei.
Aqui e ali.
Aqui eu estou vendo a capacidade do vetor e o tamanho do vetor,
aqui eu estou usando o at em vez dos colchetes.
Na posição 2 receba 10.
at(2) ou [2], poderia usar o colchetes dois tranquilamente.
Posição 2 é a terceira posição porque o vetor começa em 0.
Tanto o vetorzinho normal quanto o vector.
O vector, na prática,
fazer um vector ele cuida de um vetorzinho lá dentro, ele é alocado dinamicamente e etc,
mas é um vetorzinho, vetorzinho é uma maneira eufemista de falar.
Então, tal qual na string,
o vector pode trabalhar com colchetes ou com at.
O que nos faz pensar que
será que string, na verdade, não é um vector de caracteres?
Um boa questão para vocês investigarem.
E afinal das contas, string é um vector de caracteres?
Muito bem.
Pensem a respeito, investiguem a respeito.
Continuando aqui.
Estou imprimindo conforme vocês já viram na última execução,
depois que eu modifiquei a terceira posição, a posição 2, com o valor 10,
continuo mostrando capacity e size para ver se estão modificando.
Aqui no Vetor eu estou inserindo,
a partir da segunda posição, olhe só, begin+1,
o elemento 22, vou imprimir isso de volta,
vou testar capacity e size.
Aqui eu tento colocar na posição 100, o valor 777,
só que não tem posição 100.
Se você olhar o vetor, está aqui,
ele tem... O que aqui? 3...
Sei lá, 7 elementos ou algo assim,
inclusive a capacidade foi melhorando aqui.
Então não tem posição 100 nesse vetor.
Se eu tentar colocar em uma posição que não existe, ele dá erro.
Eu tenho que inserir as coisas
para então utilizar o operador de colchetes, ou operador de at.
Então se eu tentar colocar na posição 100 que não existe ainda,
ele iria dar erro.
Aqui eu estou usando, pessoal, uma função
chamada, aliás um comandinho, um comandinho insisto,
chamado try catch.
O try catch é o seguinte
é um comando, que eu não gosto muito francamente,
mas ele é muito útil para quando você está desenvolvendo o software,
fazer um teste do software, etc e tal.
Você faz aqui um try, você tenta, ele tenta executar sua função,
se ele não conseguir,
isso geralmente já dá erro, se eu não tivesse o try aqui,
isso daria error e terminaria a execução com erro.
Quando eu coloco em um âmbito de try o comando, ele tenta,
se o erro não for tão grave,
ele consegue voltar a execução sem parar a execução.
Então ele tenta, se ele não conseguir,
ele gera um erro que ele gerencia, recupera e volta para a execução.
Eu faço um catch, o catch permite eu tratar um erro.
Aqui quando dá esse erro eu estou criando um objeto do tipo "e",
que é um tipo padrão, um objeto padrão
tal qual o cout é um objeto do tipo ostream,
que é objeto, uma classe padrão,
eu estou criando meu "e" de uma classe padrão que é out_of_range.
O c++ tem várias classes padrões criadas por ele mesmo,
e aqui um exemplo, eu tenho o "e" de out_of_range,
isso permite contar, através do what desse objeto
e dizer: "Olha, isso está fora do limite, limite 100 não existe."
Então ele já tem alguns erros tipificados,
você pode utilizar para uma situação try catch.
Aqui em vez de usar try catch, eu programador mais antigão,
o que eu faria não é um try catch, eu faria um if mesmo, eu venho aqui,
if (Vetor.size() >= 100)
aí eu faria a operação, se não eu diria: "Olha, não dá para inserir."
Então com if else eu não deixo ocorrer o erro,
eu faço um teste antes de executar a ação,
que é muito mais razoável do que deixar gerar o erro e tentar recuperar esse erro.
Então eu não gosto desse tipo de estrutura, muita gente usa,
já entrei em vários debates, inclusive,
a cada horário com defensores do try catch ou contra,
mas basicamente o try catch pode ser bastante útil para quando você está montando o programa, testando, etc e tal,
mas, em um código limpo, eu evitaria o try catch, essa é minha opinião.
Aqui no caso dado, usaria um if,
testaria o limite, se o limite está ok,
o size>= 100, aí eu faria a operação aqui,
se não, eu mandaria a mensagem:
"Olha, não dá para fazer a operação porque não tem essa posição no meu vetor."
Bom.
Isso considerado, eu tenho aqui um erase,
que apaga um dado elemento de uma dada posição,
aqui, po exemplo, do início, aliás apago o elemento inicial.
Vamos ver se é isso mesmo.
Cadê?
Depois do erase,
sim, ele apagou o elemento inicial, apagou o 7.
Eu também posso usar erase para apagar todo mundo de um conjunto,
do início até o fim, daí ele apaga o vetor inteiro.
Olha lá, está vazio.
Aqui inclusive, depois que eu apaguei ele inteiro,
eu estou testando, se está vazio,
se está vazio, eu digo "está",
se não, eu digo "não está" e coloco palavra "vazio" depois.
Aqui eu estou utilizando o operador ternário que vocês já conhecem.
Mas em fim.
Estou fazendo aqui, inclusive, uma brincadeira com o empty.
Lembrando que os componentes da STL, como vector e list,
eles permitem usar o comando empty.
Aqui eu estou fazendo um insert,
também funciona o insert para vector.
Aqui estou imprimindo o conteúdo.
Aqui tem um clear, clear elimina tudo que tem no meu vetor.
Cuidado com esse clear.
Se você está usando um vector ou um list de ponteiros,
você faz um clear em um vector ou list de ponteiros,
uma instância de vector ou uma instância de list que tem ponteiro, e você faz um clear nele,
ele vai eliminiar os ponteiros daquela lista,
mas ele não vai desalocar memória, muita atenção com isso.
Insisto, se você tem um objeto que é uma instância
de vector ou de list que armazena ponteiros de coisas,
você faz um clear, ele vai tirar os ponteiros desse objeto em questão,
mas os ponteiros não são desalocados,
desalocação é outro procedimento.
Você provavelmente vai ter que usar um iterador na unha,
recuperar o conteúdo e fazer o delete.
Muita atenção com o clear.
Aqui está a execução do que a gente brincou com esse vector.
E aqui a gente termina o conteúdo da nossa aula.
Bom, apresentei aqui, ou reapresentei, ou aprofundei um pouco o que a gente já tinha discutido,
que é o vector e o list que existem na STL, que é standard template library, biblioteca padrão de templates.
Agora, o vector e o list não são os únicos componentes.
Além de vector e list você vai ter, por exemplo, queue que é uma fila.
Você vai ter, por exemplo, o stack, que é uma pilha.
Para quem já estudou estrutura de dados e tem uma noção,
sabe que as estruturas mais básicas vão ser lista, fila e pilha.
STL tem isso, STL tem outros componentes também.
Cito set, multiset, map, multimap,
mapa, multimapa, o set é conjunto, o multiset é multiconjunto.
Então são alguns componentes.
Até nas próximas, bem que a título de curiosidade e exemplo,
eu vou exemplificar algum desses componentes da STL
para vocês terem essa sensibilidade do quão rica é a STL,
podendo pegar lá o livro do Deitel
e mergulhar nesse universo para ter esse conhecimento, essa formação.
É legal vocês conhecerem a biblioteca padrão com maior profundidade.
que hora que você vai para outras linguagens modernas você já intui,
"Opa, essa linguagem também deve ter um conjunto de bibliotecas, tal qual o c++ tinha a STL dele."
Então isso já cria uma cultura, e cria até um reflexo
no tocante de utilização de linguagens de programação modernas
Pessoal, espero que tenham aproveitado a aula
Nos vemos brevemente na nossa próxima aula.
Bons estudos.