Pessoal, bom dia.
Hoje é dia 10 de agosto de 2020,
uma segunda-feria, 11h23 da manhã.
Estou aqui para falar com vocês sobre o grupo de slides 12,
no qual a gente trata um pouco mais de Const,
e falamos de Static, de espaço de nome NameSpace,
e de aninhamento Nested.
São os termos em inglês desses conceitos.
Certo.
Falando de const.
Vamos retorna aqui na nossa classe exemplo, a MinhaString.
A gente já tinha 3 momentos de const que estão destacados em verde.
Então você pode ter um parâmetro constante, como acontece na construtora,
você pode ter um retorno constante, como acontece no getString,
e você pode ter, eventualmente, um atributo constante.
Aqui eu coloquei esse exemplo na classe, um exemplo adicional chamado TAM_MAX
que é uma constante inteira.
A gente sabe que constantes só podem ser inicializadas dessa maneira aqui.
Então é nesse espaço entre a construtora e a implementação dela,
que você usa aquele formato de chamada de construtora de agregados.
Como vocês estão vendo aqui,
por exemplo o TAM_MAX (5000).
Esse formato de chamada de construtora
pode ser feito para qualquer objeto agregado, normalmente,
como acontece tamanho que é um objeto inteiro,
que é inicializado aqui na MinhaString.
Então estou inicializando tamanho a partir da chamada da sua construtora.
Inclusive no caso de tamanho, ele é inicializado com o retorno de uma função, ele vai chamar uma função aqui.
No caso de tamanho que não é constante, que é variável nesse caso posto,
eu poderia em vez de fazer assim fazer assado,
no sentido de colocar tamanho = strlen(s);
Esse formato usual do uso do operador recebe (=)
também seria possível para o tamanho em vez do formato de chamada de construtora.
Já no caso de TAM_MAX que é uma constante
eu só posso inicializar nesse formato.
Se eu tentar fazer TAM_MAX = 5000;, não funciona.
Então no caso de constante tem que ser via chamada de construtora.
Muito bem.
Aqui tem outra coisa importante, que está em vermelho, que é esse outro tipo de const.
Então insisto, você tinha 3 tipos de const:
parâmetro constante, retorno constante e atributo constante.
Só que tem um quarto tipo de const ainda que é a função constante.
Uma função é constante se depois da sua assinatura, ou no final da sua assinatura, melhor dizendo, aparece a palavra const.
A palavra const, todos os momentos que ela aparece na minha assinatura de função,
ela reaparece depois na definição da função.
Veja lá o const reaparecendo.
Aqui no caso do getString além de ter um retorno constante, já discutido,
ele tem, também, o const no final da sua assinatura.
No caso da assinatura já visando a implementação.
O que significa esse const depois do nome da função?
Isso significa que essa função em si, ela mesma, é uma função constante,
ou seja, significa que um função sendo constante, tem esse const depois do nome dela,
é uma função que não consegue alterar nada.
Por mais que um atributo seja alterável, como é o caso do pStr,
Se eu tentar alterar pStr aqui dentro eu não consigo.
Se eu quiser alterar tamanho aqui dentro, que é alterável normalmente,
eu não consigo porque essa função em si é constante.
Uma função const é uma função que não consegue alterar atributo nenhum.
Então para o que serve uma função const?
Para leitura, como é o caso da maior parte dos get.
Aqui eu to fazendo um get, por exemplo, que está só lendo um atributo e retornando o valor.
Não raro então os get vão ser constantes.
Não confundam retorno constante, que estou transformando aquele retorno em apenas leitura, com uma função constante.
Função constante, que é esse destacado em vermelho aqui,
o fato é que a função ela mesma não pode alterar atributo nenhum, só lê-los
Se eu eventualmente criar uma varável dentro da função constante,
aí tudo bem, a variável temporária ou a variável local o método consegue alterar.
O que o método não consegue alterar sendo constante são os atributos pertinentes a sua classe.
Legal, então isso com o atributo constante.
Vencido esse tópico, vamos para o tópico de estáticos.
Aqui nós temos atributos e métodos estáticos.
Que é uma coisa interessantíssima também.
O atributo e método estático ou static é muito peculiar.
Geralmente as classes definem os atributos de objetos,
não é isso?
Então, por exemplo, aqui eu tenho tamanho tamanho, tenho pStr.
Quando eu crio objeto "s1" na MinhaString, "s2", "s3", "s4", "s5", "sn",
cada um desses objetos de MinhaString vai ter seu tamanho e seu pStr,
ou seja, cada objeto vai ter seus atributos.
Por exemplo o objeto de Pessoa eu posso ter "Fulano", "Ciclano", "Simão", "Einstein", "Newton", "AAA", "BBB", "CCC"
que são os objetos que a gente tem utilizado lá no sistema acadêmico,
cada um desses objetos tem o atributo idade com um valor,
e esse valor é distinto, eventualmente coincidindo, mas cada qual tem seu próprio valor em si, por que o atributo é do objeto.
Não é isso?
Muito embora o atributo seja definido na classe,
quem tem de fato o atributo é o objeto.
Lembrando que a classe é o modelo dos objetos,
por isso que cada Pessoa vai ter o seu atributo idade com um valor distinto, pertinente a si.
Aqui no caso de MinhaString cada string vai ter seu tamanho, cada string vai ter seu apontamento pStr.
Não é isso?
Agora tem um novo tipo de atributo que vou chamar de atributo estático.
Que é o caso aqui, além de ser estático é constante, eu tenho nomeclasse.
Como o atributo é estático significa, basicamente, que o atributo é compartilhado
entre todos os objetos, entre todas a instâncias das classe.
Então nomeclasse vai ser um atributo compartilhado entre todos.
Se você olha lá, o atributo estático é inicializado nesse formato aqui:
eu inicializo ele direto a partir da classe por quê?
Porque ele não é um atributo de objeto, é um atributo de classe, insisto.
Aqui eu estou inicializando esse vetor de carácteres com "MinhaString".
Aqui é o único momento que posso utilizar o operador recebe,
hora que estou, realmente na prática, definindo efetivamente e recebendo aqui o valor.
Aqueles detalhes de vetores de carácteres que a gente que tem só momentos bem específicos
que você pode utilizar operador de recebe,
na prática é só na inicialização propriamente dita da definição e criação do vetor de carácteres.
Mas isso é assunto a parte.
Aqui o que eu estou discutindo é atributo estático.
É que atributo, insisto, que é compartilhado entre todos os objetos.
Por exemplo, faz sentido o nomeclasse, se eu quiser ter dentro do objeto o nome da classe,
um array, um vetor de carácteres,
vai ser compartilhado entre todos os objetos, porque é algo comum entre eles.
Não tem porque eu armazenar n vezes o nome da classe,
armazena uma vez só através do atributo estático.
Aqui eu tenho também uma função estática
que é o static const char, esse é o retorno da função,
a função ela mesma é a getNomeClasse.
O que uma função estática faz?
Uma função estática acessa um atributo estático.
Então aqui eu estou retornando o nome da classe, o nomeclasse.
Veja que a palavra static aparece só na definição do .h, não aparece no .cpp,
ou de maneira mais precisa, ela aparece só na definição e não aparece na implementação.
Insisto, o static aparece só na definição e não na implementação.
Diferente do const que vai aparecer tanto na definição quanto na implementação.
Muito bem, no tocante das funções bem entendido.
O const e o static, tocante das funções e também
o tocante de sua própria inicialização, veja que aqui não aparece o static.
Está definido na .h mas não está definido no .cpp no caso dado.
Legal.
Eu tenho um exemplo um pouco mais convincente sobre o static.
Aqui eu tenho um estático não constante.
Um estático variável, porque o estático pode variar,
única coisa é que se ele variar, vai variar para todos os objetos, não só para um.
Se eu mudar o atributo cont que é um inteiro
para um objeto, ele muda para todos os demais objetos.
Aqui eu tenho um cont, eu tenho getCont estático.
Professor, e se eu não usar função estática aqui,
deixar uma função normal,
ele consegue ainda assim acessar o atributo estático?
Consegue, consegue sim.
Atributo estático consigo acessar via função estática e também via função membro normal, bem entendido
Só que tem uma vantagem se a gente quiser colocar sem o tocante da função estática.
A função estática tem vantagem de poder ser chamada a partir da classe e não a partir do objeto,
coisa que a gente vai ver daqui a pouco no código,
e ela tem uma peculiaridade que a função estática, se ela for estática,
ela só enxerga atributos estáticos.
Essa função aqui getCont, por exemplo, não vai enxergar tamanho ou sPtr.
ela vai enxergar apenas atributos estáticos como nomeclasse e cont.
Aqui inclusive, esse nome não está legal,
a gente pode corrigi-lo depois para pStr.
Pouco importa na verdade, porque o que interessa aqui para nós vai ser o ponteiro.
Eu poderia melhorar o nome do ponteiro, bom, vou até faze-lo.
Bom pessoal, de fato corrigi aqui.
Fiz uma pequena pausa no vídeo inclusive e já corrigi outras ocorrências,
só para deixar harmônico, não é isso?
Provavelmente no código talvez tenha um nome não como está aqui.
Em todo caso, aqui nos slides eu deixei ele coerente.
Se você colocar esse código dos slides ele funciona normalmente.
Então está aqui o pStr, o pStr não é atributo estático,
tal qual não é atributo estático tamanho,
logo getCont não consegue acessar esse atributos,
Por quê? Porque ele é estático.
O getCont consegue acessar o que é estático como o cont
e como, eventualmente, nomeclasse se assim ele quisesse acessar.
Agora, se eu tirar aqui o estático
o getCont passa a cessar tudo,
tanto os atributos usuais quanto os atributos não usuais, digo os estáticos.
Mas o fato é que nosso código é estático.
Vocês vão ver uma vantagem de ter estático logo logo.
Bom, para o que o cont serve?
O cont serve para contar a quantidade de objetos do tipo MinhaString que eu tenho.
Assim, cada vez que eu inicializo, que eu crio, um objeto de MinhaString
o cont que é estaticamente inicializado com zero é incrementado em um,
e eu vou contabilizando, cada vez que eu rodo uma construtora de objeto esse contador é incrementado.
Lembre-se que como ele é estático quando eu modifico para um eu modifico para todos.
Assim se eu criar 10 objetos, o cont vai estar em 10 e todos vão ter o valor 10 em si.
Em si na prática porque na verdade eles compartilham o cont,
então eles acessam o cont e o cont está em 10, e é isso.
Agora, também, toda vez que eu destruir eu vou fazer um menos menos aqui, vou decrementando.
Bom, aqui tem uma aplicação disso no slide 7
onde eu crio um "S1" e um "S2".
Aqui, por enquanto, eu muito embora esteja rodando o atributo cont, nessa versão do meio não estou utilizando o cont,
estou utilizando só o nomeclasse através do getNomeClasse.
Veja que destaquei em vermelho aqui.
Como o getNomeClasse é uma função estática
eu posso chamar ela a partir de cada objeto, por exemplo "S1", "S2",
ou eu posso chamar a partir da própria classe.
É o caso aqui, vejam bem,
getNomeClasse está sendo chamado a partir do operador de pertencimento da classe, a partir da classe.
Isso é factível porque atributo estático é atributo de classe compartilhado entre objetos
e, sendo a função estática, eu posso chamar a função a partir da classe,
que a função estática é função de classe, compartilhada entre os objetos mas é função de classe.
Em sendo uma função de classe eu posso chamar a partir da classe.
Se você voltar um pouquinho, você vê que de fato a função getNomeClasse é estática.
Então, por quê? Porque é uma função de classe.
tal qual o atributo nomeclasse e o cont também
são atributos de classe, por isso são inicializados a partir da classe.
Em sendo atributo de classe que serve para compartilhar informações entre os objetos da classe,
eu posso chamar a função estática, referente aos atributos estáticos, a partir do próprio objeto
ou a partir da própria classe.
Aqui já é outra versão do main
onde eu crio, além do "S1" e "S2", um "S3", então tenho 3 objetos.
Se eu fizer um getCont aqui, agora eu estou usando o getCont, para "S1", "S2"e "S3"
eles vão imprimir todos o mesmo valor 3, 3, e 3.
Se eu chamar a partir da classe, que eu posso chamar a função getCont porque ela é estática,
insisto ela é estática,
eu posso chamar ela a partir da classe, como estou fazendo aqui, e vai dar 3 também.
Aqui volta a utilização do outro atributo estático, e constante inclusive.
Um atributo estático pode ser constante ou variável, aqui ele é constante, ali é variável.
Aqui eu estou chamando o getNomeClasse,
tanto a partir da classe quanto a partir dos objetos,
mostrando que função estática pode ser chamada a partir da classe e do objeto.
Se a função não for estática eu não consigo chamar a partir da classe.
Voltando na classe MinhaString,
eu tenho aqui, por exemplo, a função setString.
A função setString é uma função normal,
ela é normal e privada,
então vou pegar outro exemplo.
getString
A getString é uma função constante, mas ela não é estática.
Então, eu não posso vir aqui, por exemplo, e fazer MinhaString::getString, eu não posso,
dá erro de compilação.
Por que que eu não posso?
Porque ela não é estática, já getCont, por sua vez, é sim estático conforme já discutido.
Aqui tem outra variação na próxima versão da função main que é o seguinte:
eu coloquei o getCont antes da criação dos objetos.
Adivinha qual valor vai dar aqui.
Zero, esse cara é inicializado com zero.
Hora que eu rodo o código, que eu compilo o código, os objetos estáticos já são inicializados em tempo de compilação.
Ele já vai receber lá o zero.
Bom, inicializado em tempo de compilação por assim dizer,
ele é preparado em tempo de compilação para ser inicializado logo que o código for executado.
O código executado, a partir do que foi previsto no código e em decorrência da compilação feita,
ele vai inicializar os atributos estáticos.
Por isso que aqui vai dar zero.
Por quê? Porque é previsto que vai ser inicializado com zero.
Não foi criado nenhum objeto naquele momento do código para fazer esse cont mudar.
Então aqui nesse slide 9 ele vai dar zero.
Aqui, em contrapartida, que já foi criado os objetos e o cont já foi alterado vai dar 3.
Se eu pegar essa linha de código e copiar para baixo
também nessa nova impressão daria 3.
Por quê? Porque já vai ter alterado o atributo estático cont.
Alterou para um objeto, altera para todos.
Quando o "S1" é inicializado, passa para 1 o cont de todos os objetos,
depois passa para 2 para todos os objetos inicializados, em decorrência do "S2".
por fim com a inicialização do "S3", passa 3 para todos os objetos.
Aqui são mais variações,
então o getCont vai dar zero,
aqui eu criei o "S1", S1.getCont vai dar 1
e aqui imprimindo a partir da classe vai dar 1 também.
Depois num segundo momento eu criei o "S2" e "S3"
e obviamente hora que eu imprimir aqui vai dar 3, 3 e 3.
Se eu fizesse uma linha de código igual essa só que aqui embaixo,
obviamente que dá 3 também.
Legal, gente.
Exercício.
Fazer com que a classe Pessoa, do nosso "sistema de universidades",
tenha um atributo estático que permita informar quantos objetos de Pessoa, ou derivadas desta, existem no sistema.
Esse é um exercício bacana, claro que aqui você vai ter que utilizar sua criatividade.
Você vai criar um atributo cont estático inteiro dentro da classe Pessoa,
lá na construtora, tal qual eu fiz aqui em MinhaString,
lá na construtora de Pessoa você vai fazer cont++, destrutora cont--.
E aí você vai ter a partir da classe Pessoa, ou dos objetos de Pessoa, acesso à esse atributo contador.
Aí nos menus você pode criar uma opção de informar quantas Pessoas existem no sistema,
e a partir desse atributo estático, seja do objeto ou da classe,
você vai conseguir informar quantas Pessoas tem.
Retificando o discurso, é melhor você chamar a partir da classe e não a partir do objeto,
para informar a quantidade de Pessoas que existem.
Obviamente que você vai ter uma função getCont estática para você poder chamar da classe.
E você vai fazer no momento que você for apresentar a quantidade de Pessoas que existem no código,
você vai fazer Pessoa::getCont.
E por que é melhor chamar a partir da classe e não a partir dos objetos?
Se eventualmente no seu sistema você não tiver nenhum objeto criado na unha, não criado dinamicamente,
você está tranquilo porque você consegue chamar a partir da classe a quantidade de Pessoas,
estaticamente falando.
Outra questão que tem que limpar aqui, que as pessoas confundem, é o seguinte:
você tem duas maneiras de criar objetos,
ou dinamicamente através de new e depois tem que fazer o delete,
ou você faz na unha.
Tradicionalmente, a gente fala no jargão que criar um objeto na unha é criar um objeto estaticamente dentro da classe.
Mas não confunda o termo criar um objeto estaticamente, criar na unha, com atributo estático e método estático.
São coisas distintas.
Atributos estáticos e método estático é isso que a gente discutiu nessa aula.
Criar um objeto na unha, o pessoal às vezes chama de criar um objeto estaticamente e não dinamicamente.
Então criar ali localmente, criar diretamente dentro da classe.
Só para não confundir esses termos, às vezes tem termos que dependem do contexto conforme se sabe.
A língua portuguesa é assim, tem termos que dependendo do contexto significam uma coisa ou outra.
Por exemplo o termo legal.
"Ah, aquele filme é muito legal", significa que o filme é bacana,
"Ah, esse procedimento é um procedimento legal" no sentido de que ele é legalizado, que ele respeita as leis.
Então tem termos que dependem do contexto.
Aqui o caso estático, objetos criados estaticamente dentro de uma classe
significa que eles são criados na classe diretamente, criado na unha,
Não confundir com termo estático de atributos estáticos e funções estáticas que é o que a gente discutiu na aula de hoje.
Bom, acabei me repetindo um pouco aqui, mas acho que vocês entenderam o ponto.