Pessoal, boa tarde.
Aqui é o professor Simão falando.
Estamos no dia 17 de julho de 2020.
Uma sexta-feira a tarde.
O tema da exposição de hoje é o Grupo de Slides número 8.
Aonde a gente relembra sobrecarga de métodos
e aborda, particularmente, a questão de
constantes e sobrecarga de operadores.
Passamos do slide 1 para o slide 2, naturalmente.
Aqui no slide 2, desse Grupo de Slide em questão,
eu tenho o tema sobrecarga de métodos, tá.
Isso é apenas pra vocês não confundirem
com a outra temática principal dessa aula, inclusive,
chamada sobrecarga de operadores.
Sobrecarga de método é isso que tá sendo exposto aí no slide.
Aqui, por exemplo,
eu tenho a construtora sobrecarregada.
Eu tenho a construtora com 4 parâmetros
ou com nenhum parâmetro
A primeira construtora tem 4 parâmetros, sendo 1 default.
Então, eu posso acionar a construtora com 4 ou 3 parâmetros
ou sem parâmetros que é essa aqui.
Eu posso sobrecarregar outras funções também.
Como as funções membro aqui dessa classe Pessoa,
particularmente, o Calc_Idade que tá aqui exemplificado.
Eu tenho um Calc_Idade com 3 parâmetros
e outro com 1 parâmetro.
Aí, sem nenhum valor default.
Se eu chamar, a partir de um objeto da classe Pessoa,
a função membro Calc_Idade com 3 parâmetros
ele ativa alia a primeira função membro.
Se eu passar com 1 parâmetro ele ativa a segunda função membro.
Se eu passar com algo diferente de 3 ou 1 parâmetro,
parâmetros apropriados, né.
se eu passar com algo diferente disso, obviamente vai dar erro,
e vai dar certo se eu passar com 3 ou 1 parâmetro
claro que parâmetros apropriados, bem entendido.
Então, ele se localiza pela,
na chamada da função sobrecarregada, pelo número de parâmetro
em sendo parâmetros corretos,
e isso é sobrecarga de método
que algumas pessoas chamam de
polimorfismo de função.
Apesar de eu não gostar do termo,
porque pode confundir com o polimorfismo mais propriamente dito.
Então, eu na minha linha de oratória, de exposição,
sempre vou chamar isso de sobrecarga de método que é
um termo amplamente aceito na literatura.
Isso considerado,
agora passamos pro tema principal da aula
que é const e, particularmente, sobrecarga de operadores.
Aqui, nós temos
um exemplo de classe que vai ser chamada Classe String
Isso me serve só de reflexão inicial, pelo seguinte,
a gente vai construir uma classe MinhaString.
Tá vendo, ó.
Classe MinhaString.
Enquanto que
já existe no C++ uma classe chamada string, tá.
Então, vejam só, o que eu tô querendo dizer aqui é o seguinte,
eu vou construir uma classe MinhaString
que querendo ou não, é inspirada na classe String aí apresentada.
Como eu dizia, retomando,
pra que fique mais fácil de eu entender o que eu quero construir
com uma classe MinhaString
nada obsta, nada impede
de antes de eu construir a classe MinhaString dar uma lembradinha
em vocês do que que é a classe String.
Eu já falei isso aqui na última exposição,
na exposição que eu falei de arquivos binários, tá,
mas vou lembrar rapidamente.
A classe string é uma classe preexistente no C++, tá.
Lá no #include .
Você pode criar objetos de MinhaString,
como s1 e s2 que estão aí expostos, tá.
Essencialmente, um objeto MinhaString ela vai cuidar de um vetor de caracteres.
Aqui o s1, por exemplo, tá recebendo
esse vetor "bom dia!".
Já o s2, tá recebendo, aqui, vazio.
Só tem lá o '\0', né,
que denota a finalização de vetor, tá.
Agora eu posso fazer aqui o s2 receber o s1.
Daí o s2 passa a ter um "bom dia!" também.
Aqui eu tô imprimindo, através do operador de fluxo, né.
o s1, depois o s2, ou ambos vão imprimir "bom dia!",
então, você tem um duplo bom dia aqui e pula a linha.
No vetor de caracteres, aliás no string,
eu posso acessar, no objeto string,
por exemplo o s2,
tal qual eu faço com um vetor de caracteres no tocando ao operador de colchetes.
Tá aqui, ó.
Então, tô acessando na posição 0
do vetor tratado por s2
tô mudando o bzinho por um Bzão.
B maiúsculo.
Aqui, obviamente, tem um errinho de comentário,
a gente já vai ajustar, pois não existe um s3.
Bem entendido.
O fato é que eu modifiquei ali, o s2, na posição 0.
Se vocês olham as linhas subsequentes, o que que vai acontecer?
Eu vou imprimir o s1 e o s2,
agora eu vou ter bom dia com minúsculo e Bom dia com maiúsculo,
porque o s2 foi modificado nessa posição inicial ali.
A hora que eu faço s2[0], na verdade, eu tô acessando a string
aliás, o vetor de caracteres que esse meu objeto string trata.
Na prática, tá, como o objeto trata o vetor de caractere
usar esse operador de colchete permite eu criar uma certa transparência,
como se eu tivesse acessando diretamente o vetor de caracteres,
que tá encapsulado por esse objeto aqui.
Essa que é a ideia.
É eu olhar pra string como se ela fosse, de fato, um vetor de caracteres ela mesma.
Não é isso?
Então, eu posso acessar, por exemplo,
eventualmente o s1,
usando o operador de colchetes
e aí, eu vou ter acesso a essa string "bom dia!" aqui.
O usuário do objeto não precisa nem se recordar que, na verdade, é um objeto
cuja essência é "bom dia!".
Ele colocando s1 e colchetes de 0, 1 e etc,
ele acessa as posições direto como se fosse o próprio vetor o s1,
mas que, na prática, é um objeto do tipo string.
Aqui, ainda, eu posso ter uma série de propriedades com relação
a objetos do tipo string.
Como o length que me dá, aqui, o comprimento da coisa.
O que mais?
Posso acessar também através do operador de colchetes,
não só com constantes, mas também com variáveis, tá.
Com índices pra dinamizar os meus acessos, né.
Então, aqui eu tô imprimindo o conteúdo de s2 caractere a caractere,
usando o colchetes, tá.
Através desse for aí que é a mesma coisa.
Vocês viram que eu imprimi s2 caractere a caractere aqui,
e também imprimir, ali, ele na integra.
Por que que é possível eu imprimir caractere a caractere e também imprimir na integra?
Ou mesmo fazer esse tipo de operação aqui?
Por quê?
Porque o... a classe string,
sobrecarregou, ou seja, preparou os operadores
de recebe, de fluxo e de colchetes
pra trabalhar no tocante aos seus objetos.
Insisto, a classe foi preparada pra usar esses operadores
operador de recebe,
operador de colchetes
e operador de fluxo.
Nós, justamente, vamos ver parte desses operadores
sobrecarregados na classe que vai ser não string,
mas vai ser MinhaString.
A intenção
fazer uma classe que explique pra vocês um pouquinho
como é que essa classe foi programada aqui no C++ originalmente.
Claro, tá, essa vai ser uma versão nossa,
com as peculiaridades nossa,
mas que vai permitir aí, através de um exemplo pedagógico,
a introduzir alguns conceitos pertinentes.
sobre constância, sobre constantes
e sobre, particularmente, sobrecarga de operadores, tá.
Então, eu vou permitir vocês criarem objetos assim, ó.
s1, s2, s3, s4, sn,
que vai ser do tipo MinhaString, daí,
não string.
Também vai poder utilizar o operador, por exemplo, de recebe,
vai também poder utilizar o operador de fluxo.
Aqui eu tenho uma versão 0 dessa classe MinhaString.
Queria salientar que essa classe MinhaString foi inspirada
num exemplo que tem lá no livro dos Deitel.
A maior parte dos meus exemplos, inclusive dos slides anteriores, né,
eles são exemplos criados com a minha própria imaginação, criatividade, tá,
mas algum exemplo ou outro,
a gente também pode se inspirar na literatura, porque não.
É o caso aqui da classe MinhaString.
Ainda que seja bem peculiar, porque a gente fez
uma série de nuances que são próprias dessa implementação, né.
Começando pelo nome que tá em português, mas há outras coisas muito mais de âmago
do que apenas de forma.
Aqui tá uma primeira versão, versão 0, né.
da classe MinhaString
Você tá vendo que aqui
eu tô usando um outro padrão linguístico nesse exemplo,
talvez até à luz da inspiração do livro.
Eu tô colocando primeiro a parte pública e depois a parte privada.
Isso é possível, tá.
Posso criar: privado, público; público, privado.
Eu posso, se quiser, colocar privado, público, privado.
público de volta, protected.
Posso ir criando várias seções
de privado, público e protected
conforme eu vou necessitando, tá.
Claro que o mais correto é você organizar
uma seção de público, uma seção de protegido,
uma seção de privado.
Pode começar nessa ordem que eu falei ou, justamente, na ordem inversa
conforme o padrão linguística que a sua equipe de trabalho esteja aplicando.
Aqui, já começo fazendo uma observação,
que nessa construtora sem parâmetros aqui, ó,
deveria ter, bem entendido,
inicialização de atributos, tá.
Então, façam isso na versão de vocês.
Aqui eu tenho um versão, ainda, que está dentro
da própria classe, ou seja, uma função implementada inline.
A, professor, então é possível você misturar
funções que são implementadas no .h e funções membro que são implementadas no .cpp?
É possível.
É possível e acontece.
Particularmente quando as funções são bem pequenininhas
as vezes o pessoal coloca no .h direto.
Ainda que o ideal é seguir um padrão,
conforme vocês estejam trabalhando com uma equipe
que defina um padrão.
Tem diferença entre definir uma função dentro da classe,
ou seja, implementar ela di... no sentido de implementar ela no próprio .h
em relação a implementação no .cpp?
Bom, primeiro tem a questão de organização, né.
Vejam que aqui, ó.
Aqui já cria uma poluição, porque você vai ter uma implementação aqui no meio.
E tem outro detalhe, tá.
Do ponto de vista técnico,
se pensar na execução dos objetos,
quando eles estão implementados no .cpp,
os métodos tão implementados no .cpp por default,
lá na baixaria computacional,
aí os objetos vão compartilhar os métodos.
Ele, muito embora, a gente diga filosoficamente falando que cada objeto tem o seu método,
na prática, ele vai compartilhar os métodos,
se passando de parâmetro pra...
pro método que ele tá utilizando de maneira compartilhada
com os demais objetos da mesma classe.
Insisto, no que eu tô dizendo. Se a implementação for no .cpp
do método, o método é compartilhado entre os objetos
na baixaria computacional.
Se for implementado no .h
aí, por default, cada objeto tem uma cópia do método pra si,
aí, depende se você tá querendo trabalhar
mais do ponto de vista
de economia de processamento
ou de economia de memória.
Se cada objeto tem o método em si
ele vai gastar mais memória, mas
pode ser um pouquinho mais rápido o acesso de processamento, tá.
Porque provavelmente vai evitar uma série de conjuntos de passagem de parâmetros,
lá na baixaria computacional bem entendido.
Agora, se você tá querendo economizar memória,
aí, você pode compartilhar os métodos utilizando implementação no .cpp,
e aí gasta um pouquinho mais de processamento, pelos motivos já explicados.
Então, eu tenho a função inline, tá.
Então, implementada direto na classe,
direto no .h, bem entendido.
E aqui tem a versão
com a implementação na respectiva .cpp.
Eu também posso...
Perdão. Deixa eu voltar aqui.
Eu também posso fazer uma variante aqui, tá, gente,
que é o seguinte.
Eu poderia, se eu quisesse,
vir aqui e fazer aqui no .h.
Isso também é possível, tá.
Tô usando aqui o operador de pertencimento ali, tá.
Esse seria o equivalente a implementar direto aqui, tá.
Equivalente a implementar direto aqui.
Permite que a função seja implementada no .h
só que um pouquinho mais organizado, colocando as implementações lá pra baixo.
O resultado de implementar assim ou assado,
é o mesmo em termos de inline, ou seja, na linha
no sentido que cada objeto vai ter a sua função.
Porque eles tão implementados no .h.
Seja direto ali, logo após a definição do método,
seja logo abaixo, aqui, pós a definição da classe como um todo
implementando o método no .h, ainda que de maneira até um pouco mais organizada,
porque você tá utilizando daí o operador de pertencimento
e colocando abaixo da definição.
Só que a gente tá optando por essa versão aqui
de implementar no .h e no .cpp, né,
porque a gente acha que fica mais organizado
e também agora você sabe que tem essa diferença técnica.
Então no .cpp, tá, por default, por padrão ele vai
compartilhar, como está aí neste momento na tela, tá,
ele vai compartilhar essas funções
entre os objeto lá na baixaria computacional.
Isso considerado,
voltemos aqui ao âmago do exemplo.
Construtora sem parâmetros.
Implementação inline.
Construtora com parâmetro
com implementação não inline, não na linha
implementado na respectiva .cpp.
Tá aqui, ó.
Ela recebe um vetor de caracteres por parâmetro.
Descobre o tamanho desse vetor de caracteres
usando a função strlen do string.h,
lá, herdado da linguagem C.
Descobrimos o tamanho.
Aqui, daí, eu vou copiar o conteúdo que tá vindo do parâmetro
para o meu atributo str.
Atributo esse que tá aqui embaixo.
Tanto o str que tá aqui,
que é um vetor de 300 caractere... caracteres,
quanto a variável tamanho que é uma variável inteira.
Então, eu armazeno o tamanho do vetor
e armazeno o vetor.
Você tem que lembrar que tal qual um objeto da classe string
preexistente do C++,
um objeto de MinhaString vai servir basicamente pra gerenciar um vetor de caracteres.
E essa versão aqui é chamada de zero não à toa,
porque ela é uma versão bastante simplória, tá.
É quase... qual o termo? Irrisória.
E aí, depois, na versão 1, 2 ela vai evoluindo, ganhando corpo
e a classe ganha sentido no tocante ao gerenciamento
de vetor de caracteres.
Temos aqui também a destrutora
que não está implementada inline, mas implementada lá no .cpp, né.
A implementação dela é uma implementação vazia.
A gente sabe que mesmo que seja vazia a gente explicita
a implementação de destrutora,
porque se a gente não fizer isso o C++ faz pra gente
e a gente sempre quer evitar que o C++ faça coisa que a gente não sabe o que tá acontecendo, né.
Preferencialmente, muito embora, nem sempre seja evidente.
Aqui tá o getString.
O que o getString faz?
Ele retorna o vetor de caracteres, por assim dizer.
Vejam que aqui, na verdade, o que ele tá retornando, tá.
É o str.
O que que é o str?
str é um ponteiro de caractere
pro início do vetor.
Então, o str, na verdade, é a posição 0.
É o endereço da posição 0 do vetor.
É a primeira posição.
A gente diz assim, em termos coloquiais,
que eu tô retornando um vetor, mas na verdade eu tô retornando um ponteiro pro início do vetor.
E isso tem impactos conforme a gente vai discutir.
Inclusive, no nível de acesso
das informações que estão dentro de cada objeto dessa classe.
Legal.
Tá aí exposta, tá, a versão 0.
Que eu já filosofei bastante a respeito dela,
mas trazendo informações úteis pra vocês espero.
Aqui tá a utilização no main da classe MinhaString
Tô criando um vetorzinho aqui, tamanho 100.
Com esse conteúdo.
Crio o objeto S1 de MinhaString passando o vetorzinho
e depois eu faço um S1.getString
pra provar que o S1 tá conseguindo cuidar desse conteúdo aqui.
Eu poderia pegar, em vez de criar um objeto,
pegar essa constância aqui,
essa informação "Minha primeira string soh minha." e passar direto aqui dentro?
Poderia tranquilamente, tá.
Funcionaria sem problema nenhum.
Inclusive, a gente pode até criar um versão aqui, tá.
Isso aqui só pra constar nos autos.
Isso aqui, gente, perfeitamente viável.
Então, eu posso utilizar a classe MinhaString, assim como eu tô mostrando pra vocês
ou posso utilizar, ainda, desse jeito aqui, ó,
criando o vetor de caracteres
e aí, passando o vetor de caracteres para o objeto.
Vejam que quando eu crio vetor de caracteres usuais, né,
ordinários, no sentido de não serem extraordinários, no sentido de comum,
o único momento em que eu posso usar o operador de recebe com vetor
é nesse momento aqui
aonde eu acabei de criar o vetor
e já tô dizendo que esse vetor é isso.
Então, é a única maneira que eu uso operador de recebe.
Eu crio o vetor com tamanho dado e já coloco um conteúdo nele.
Só assim que eu posso usar operador de recebe.
Porque daí eu já tô dizendo que, na prática,
esse cara já aponta pra esse conteúdo ali
e tá resolvido.
Eu não posso usar operador de recebe
se for numa linha subsequente.
Insisto, só posso usar o operador de recebe nesse momento aqui de inicialização do vetor.
Então, no próximo slide,
tem aqui, ó. Se eu for inicializar
depois que eu já criei o vetor
aí não tem como eu associar
que isso aqui
seria o vetor pelo operador de recebe de uma segunda linha.
Tem que ser numa linha única, conforme eu fiz no slide anterior.
No agora slide 9 que tá ali, anterior.
Nós estamos no 10.
Bom, se eu for inicializar o vetor numa segunda linha
aí, eu sou obrigado a usar o comando strcpy
conforme você tão vendo aqui, tá.
Mas isso é só lembrança de conteúdo, lá, de Fundamentos de Programação 1,
Computação 1 ou coisa que o valha.
E aqui, a inicializa normalmente
criou meu objeto S1 inicializando normalmente com o vet.
Então, aqui foram 3 versões criando o objeto S1, inicializando ele
seja direto com um conjunto de caracteres,
que vai formar um vetor de caracteres em background,
seja explicitando o vetor de caractere
já inicializando ele direto
ou explicitando o vetor,
inicializando numa segunda linha que é o caso que vocês tão vendo em tela
e, daí, passando o vetor aqui pro construtor do objeto,
e depois imprimo a coisa.
Agora eu vou começar a melhorar essa versão, pra essa versão até ganhar corpo
e fazer sentido na sua própria existência, tá.
Versão que eu digo da classe MinhaString
que tá permitindo criar objetos aqui que gerenciem
o conteúdo de vetor de caracteres.