Versão 1.
Insisto que
essa classe MinhaString é inspirada no livro dos Deitel, tá.
Pai e filho Deitel.
Tá aqui a classe MinhaString.
Tá aqui a construtora sem parâmetros
inline, na linha.
Na qual eu já estou inicializando os atributos de maneira mais decente.
O tamanho recebe 0,
e aqui, o str lá,
tá recebendo uma string vazia
que, na prática, tá constituindo um \0
que é aquele sinalizador de final de vetor de caracteres
que vocês já estudaram na linguagem C.
Outra coisa que eu tô fazendo bastante pertinente, tá.
É, agora, na construtora com parâmetro
fazendo com que o parâmetro seja constante.
Inclusive, muitos de vocês, em alguns compiladores,
já estavam utilizando isso, tá.
Tem alguns compiladores modernos
se você não fizer aí a configuração certinha do ambiente
e colocar apenas o char s, aqui, colchetes,
ou char* s, tanto faz, sozinho
eles reclamam.
Ele tá obrigando você a colocar uma constância aqui.
Geralmente, os compiladores aí, até uns 4 anos atrás
não tinha essa... não vinha com essa configuração default
de obrigar a utilizar constância nesse tipo de situação,
mas a coisa evoluiu.
Bom, muitos de vocês quando o compilador reclamou, vieram e colocaram o const aqui na frente,
porque viram alguns tutoriais na internet,
porque os monitores falaram,
porque eu mesmo os ensinei nesse sentido.
O que acontece quando você coloca um const aqui na frente, no final das contas, tá.
E porque que algumas configurações do compilador já estão exigindo que isso seja assim?
Pra evitar que você venha aqui
dentro e altere o s.
Supostamente você não quer alterar o s aqui dentro.
Se eu alterar o s aqui dentro
o compilador vai gritar e dizer: não, não pode.
Esse aqui é um parâmetro que tá vindo
que ele é constante
ele serve apenas pra ser lido
seja pra aqui pra informar o tamanho
do vetor de caracteres
seja pra esse vetor ser lido
copiado pra outro vetor
que na prática vai ser nosso atributo interno que vai gerenciar aqui as informações, tá,
mas o s ele mesmo, o parâmetro ele mesmo,
ele não é alterado em nenhum momento aqui
e nem deve nesse contexto dado,
por isso que eu tô usando uma constante.
Dito outramente.
Sempre que um parâmetro
servir apenas pra alimentar algo
como um atributo
e esse parâmetro, não existir a intenção desse parâmetro ser alterado nesse momento
no âmbito da função que ele existe,
esse parâmetro deve ser constante.
Por quê?
Porque tudo que não for ser alterado
ele deve ser explicitamente constante.
Porque se eu tentar alterar por equívoco, por exemplo,
aí o compilador detecta e diz:
opa, você não pode alterar algo constante
e evita uma série de erros.
Então, sempre que for possível, deixar uma coisa constante
deixe, explicitamente.
Dado que é uma boa prática,
que permite você localizar eventuais
mudanças feitas por equívoco, por exemplo.
Continuo tendo a destrutora sem código, aqui, vazia, né.
Por motivos já explicados.
E tenho o getString
que tá retornando, aqui, a string por assim dizer.
Na verdade, ele tá retornando o ponteiro pro início aqui do str.
Lembrando que o nome de um vetor, seja ele de caractere, ou qualquer vetor,
é um ponteiro pro início daquele vetor.
À luz de toda a formação que vocês têm
em Fundamentos 1, Computação 1 e afins.
Vamos ver a utilização aqui então,
dessa MinhaString versão 1 agora.
Tá ali, ó.
Tô usando aquela abordagem de criar um vetorzinho e já inicializá-lo.
O único momento que posso usar o operador de recebe
pra constituição de vetor, insisto,
à luz da formação que vocês têm
em Fundamentos 1, Computação 1
do conhecimento da linguagem C, enfim.
Aqui tô criando o objeto S1 de MinhaString
que vai gerenciar no seu âmago uma cópia do que tem em Vet.
Conforme vocês viram ali na implementação da construtora com parâmetro.
E aqui eu vou imprimir o conteúdo
do vetor gerenciado por S1.
Vejam que a hora que eu tô retornando getString... que eu faço getString, aqui.
O que o S1 tá retornando é um ponteiro
pro âmago...
pro âmago do vetorzinho gerenciado por S1.
Lá dentro do S1 tem o str
a hora que eu faço getString,
na verdade, eu tô permitindo ao cout entrar no âmago do objeto
através de um ponteiro e ler o conteúdo que tá lá dentro.
Bem entendido.
A gente vai debater isso aqui melhor daqui a pouco, tá.
Classe MinhaString versão 2. Já estamos melhorando a coisa.
Deixa eu voltar aqui, eu tô no slide 14,
deixa eu voltar no 13, aliás, vou voltar no 12.
Vejam que aqui eu tô utilizando
o formato de operador de colchetes, tá.
Pro vetor de caracteres que é chegado por parâmetro.
Não tem problema nenhum.
Essa é uma versão mais em alto nível,
mas a gente que no final das contas
se eu tô passando um vetor por parâmetro
isso aqui, na prática, é um ponteiro.
Por isso que lá no slide
14, aliás, no 15 que vai apresentar a versão 2 da classe MinhaString,
você já pode explicitar diretamente
que o parâmetro na prática é um vetor de caracteres, tá.
É mais honesto, vamos dizer assim.
Você já explicita ele
já trazendo uma visão um pouquinho mais baixo nível
dizendo: olha, esse parâmetro, na prática, é um ponteiro.
Eu vou acessar o que vem por parâmetro
através de um apontamento
e através desse apontamento que eu vou copiar
pra outro endereçamento de memória
que é o ponteiro str que me dá acesso a isso aqui.
Então, aqui, eu tô deixando o parâmetro
no formato mais explícito de ponteiro
e, à luz da minha experiência profissional,
eu vejo que o pessoal utiliza, realmente, mais nesse formato mesmo.
Aqui, também tô salientando
que eu tô pegando, aqui, a variável string...
Perdão, a variável inteira tamanho.
Variável inteira tamanho, insisto.
Ou o objeto tamanho da classe inteiro, da classe int.
E tô fazendo esse
objeto, essa variável, no caso,
receber o tamanho, o número que é o tamanho
do vetorzinho que tá vindo por parâmetro.
Pra que isso aconteça na prática, tá,
a classe inteira sobrecarregou o operador de recebe aqui
a classe inteira permite, outramente dito,
que se utilize o operador de recebe, pra receber um número.
Obviamente que o strlen(s) retorna um número.
Por isso que o operador recebe pode receber dessa função
A hora que o operador de recebe recebe uma função,
na prática, ele tá recebendo o retorno da função, bem entendido, tá.
Isso já é largamente sabido.
Pra eu fazer o tamanho receber um número
ou eu uso o operador de recebe ou uso o construtor
do objeto tamanho da classe inteiro, tá.
Conforme eu vou mostrar no próximo slide
que vai ser o slide 16.
Tá ali, ó.
Posso usar o construtor do objeto inteiro,
também funciona.
Nesse espaço aqui, que a gente lembra o nome da construtora
que ela pertence a classe pertinente, né, que é a MinhaString
e a implementação.
Nesse espaço aqui eu posso fazer o quê?
Posso chamar a construtora de objetos agregados,
que é o que tá acontecendo aqui.
Tô chamando a construtora do objeto inteiro tamanho,
e a construtora de classes base
MinhaString não tem classe base.
Então, aqui eu só posso chamar a construtora, nesse âmbito
de objetos agregados que é o caso do tamanho aqui.
Professor, mas tamanho é um tipo predefinido? Sim.
Tamanho é um inteiro predefinido
e o inteiro pra nós aqui
do ponto de vista do C++, tá.
É um objeto da classe inteiro
por isso que eu chamei a construtora ali inclusive.
Destrutora tá aqui.
O getString tá aqui retornando o ponteirinho de str.
Poeticamente falando retornando a string.
Agora, vamos fazer uma pequena alteração,
salientar em vermelho
esse discurso que eu já tô falando
sobre o retornar aqui do str.
Poeticamente falando, como eu dizia,
isso tá retornando string, né, str,
mas, na prática, conforme já discutido,
isso tá retornando um ponteiro pro início do vetor,
e muita atenção com isso, tá.
Muita atenção com isso,
porque isso aqui pode ser fontes de erro de toda ordem.
Inclusive, fonte de invasão
no âmago desse objeto, tá.
Alguém pode fazer uma ação hacker aqui, no tocante desse objeto
Vou mostrar isso no próximo slide.
Tá aqui, ó.
Criei um objeto S1 de MinhaString,
agora já passando diretamente, aqui,
o conteúdo que vai ser gerenciado, né.
Copiado e gerenciado pra dentro do S1.
Na prática, o que ele faz é criar um vetor de caracteres temporário
que é passado por parâmetro aqui
daí aquele ponteiro dentro da construtora, dentro da função construtora
vai navegar aqui
e permitir copiar o conteúdo lá,
pro str dentro do S1.
Tanto é que depois, a hora que eu venho aqui, e faço S1.getString ele imprime lá, o conteúdo de str.
Mas, na verdade, o que eu tô passando pro operador de fluxo aqui do cout,
não é o vetor,
é o ponteiro pro vetor.
Na parte, aqui, esse operador de fluxo
o que ele faz
é entrar dentro do objeto e navegar dentro do vetor.
Vou dar outro exemplo.
Tô procurando aqui um ponteiro ps.
Quando a gente cria um ponteiro,
a gente sempre pode,
por alegria, né, por segurança
inicializar ele com nulo, tá.
Por que não?
Mas logo em seguida,
por isso que esse código não apresentava problema,
eu já faço o pS,
que é um ponteiro temporário, ponteiro de caractere,
receber S1.getString.
O que o getString traz pra gente?
Traz str, ou seja, traz o apontamento pro início desse vetor de caractere.
Professor, deixa eu ver se eu entendi direito.
Então, o pS tá acessando um atributo que devia ser privado do objeto?
Está, porque está usando o ponteiro e ponteiro permite baixaria computacional.
Então, você tá acessando uma área privada que você não deveria acessar,
mas como o ponteiro é ponteiro, você tá acessando a coisa via ponteiro.
Aí, vejam só o que acontece.
Se eu vier aqui...
Bom, se eu fizer um ponteiro receber
o início de um vetor de caractere.
Você sabe, pelos seus conhecimentos de linguagem C,
que esse ponteiro passa a se comportar como vetor ele mesmo,
sendo que eu posso utilizar, inclusive, o operador de colchetes
Olha lá, ó.
Então, aqui o pS posição 0,
na prática é o str,
de dentro do objeto S1, posição 0.
Vou receber um T maiúsculo
e a posição 1 vai receber um i maiúsculo,
e aqui eu vou ter um TInha minha string soh minha.
Agora a MinhaString só minha não é mais do S1
ela é acessível fora também.
Eu posso fazer isso, tanto via operador de colchetes,
quanto através da aritmética de ponteiros, tá,
que é essa segunda linha aqui que eu coloquei só a título de curiosidade.
Se eu comentar essa e descomentar essa, o resultado vai ser o mesmo, tá.
Salve o fato que aqui tem uma duplas-aspas,
e ali devia ser "aspas normais".
Vou corrigir isso aqui.
Na verdade, apóstrofos, né.
Legal.
Agora vai imprimir aqui, tá, gente.
TInha uma string soh minha.
Por quê? Porque aqui eu modifiquei
o núcleo do S1,
utilizando um ponteiro,
O ponteiro pS que vai lá, que recebe o
o núcleo de S1 que é o str,
ele recebe o endereço str e acessa via ponteiro invadindo a essência do objeto.
Então, isso é muito peculiar, tem que tomar muito cuidado com isso aqui, tá.
Por isso que eu criei uma versão 3 da MinhaString.
Aonde agora o meu retorno vai ser constante.
Não só a construtora vai ter parâmetro constante,
como a função getString vai ter retorno constante.
E isso é fundamental, tá.
Tanto quanto possível, particularmente, quando você estiver usando ponteiro,
transforme o retorno em constante.
Se a tua intenção no retorno desse ponteiro
é permitir que quem está recebendo o ponteiro apenas leia a informação e não altere.
Que é a maior parte dos casos, suponho eu, tá.
Em todo o caso figura-se, a intenção é
que quem esteja recebendo esse ponteiro apenas leia a informação,
então o retorno é constante.
Eu tenho aqui como constante, tá,
e a gente tá falando de constância.
Parâmetro constante. Os parâmetros podem ser constantes, tanto quanto possível.
Se o parâmetro é intencionado pra não ser alterado,
explicite ele como constante.
Se o retorno, particularmente ponteiro,
é intencionado pra não sofrer alteração,
servir só pra leitura,
coloque como constante.
Se um atributo, inclusive,
vai ser inicializado com um valor
e não vai mudar de valor
durante o ciclo de vida do objeto,
momento de vida do objeto,
coloca ele como constante também.
É o que eu faço no próximo slide no tocante ao atributo tamanho.
Ele passa a ser constante.
O tamanho não altera.
Nesse contexto que tá exposto no quadro, aí, no slide
o atributo constante (tamanho) não altera,
então ele pode ser constante.
Professor o vetor também não, o vetor str.
Ele até poderia ser constante, ainda que aqui tenha um conjunto de nuances,
mas, aqui, o meu ponto é trazer pra vocês que
se tiver um atributo e não tiver dificuldade maior
deixá-lo como constante,
então é só ir no const direto,
se ele não for alterado no ciclo de vida.
Isso aqui serviu como exemplo, tá.
Daqui a pouco eu vou evoluir essa MinhaString
e aí, eu vou precisar que o tamanho volte a ser não-constante,
porque vai variar, aqui,
str e o tamanho da str
no ciclo de vida do objeto.
Em todo o caso, por enquanto o tamanho é constante conforme vocês tão vendo,
e o retorno, aqui, também é constante, tá,
do getString.
Aqui, eu tô inicializando, dentro do S1 já inicializando.
Ele vai ter uma string de
tamanho constante dentro de si que ele gerencia.
Aqui, eu criei um pS.
pS que é um ponteiro constante de char.
O que significa um ponteiro constante de char?
É um ponteiro que consegue apenas ler e não consegue alterar.
E por que que eu tô criando um ponteiro constante de char?
Porque eu tô fazendo o pS receber getString
que retorna ponteiro constante de char.
Se eu colocar só ponteiro char aqui.
Arrancando esse const daqui, tá. Dá erro.
Dá erro, porque...
o getString só consegue retornar pra quem seja leitor constante,
ou seja, quem vai fazer um acesso constante
no caso, aqui, dos caracteres.
Então, por isso que tem que colocar assim.
Em sendo constante, a coisa toda,
se eu tentar fazer esse código que tá comentado aqui, ele vai dar erro.
Se vocês descomentar e executar isso aqui, vocês podem fazer o teste aí na...
na base de experimentação de vocês,
nos projetos de vocês, tá.
Isso aqui daria erro.
Agora, isso aqui não dá erro quando em contrapartida aqui.
Eu tô criando um segundo vetor, de 2000 caracteres que seja
Eu tô copiando o conteúdo de pS
ou o conteúdo do cara apontado por pS,
bem entendido,
pra vet2,
mas aqui eu tô copiando, isso significa que eu tô lendo e criando um igual
eu não tô modificando o pS.
Então, isso é importante.
Aí, eu posso fazer toda hora de leituras
no tocante ao conteúdo apontado por pS.
Por quê? Porque foi retornado pra mim aqui no getString um const char.
Eu não posso alterar.
Então, isso aqui é fundamental, tá.
Prestem bastante atenção com esses retornos de vocês com os gets.
Gets tão constante quanto possível o retorno.
Avancemos na classe MinhaString pra uma versão 4.
É uma versão mais peculiar.
Essa versão 4 aqui, ela vai ter uma mudança interessante.
Se você voltar um pouquinho, eu tô no slide 23,
voltar pro slide 21, que seja.
Você tá vendo que o str, aqui,
é um vetor de 300 caracteres.
Isso significa que se eu tentar colocar um poema aqui,
sei lá, Pasárgada.
Tentar colocar o poema aqui lá da Vou-me embora pra Pasárgada
provavelmente vai passar os 300, aliás, vai passar os 300 caracteres
então, eu não poderia gerenciar com o objeto de MinhaString lá Pasárgada,
ou qualquer texto mais longo, maior que 300 caracteres.
Por isso que eu vou criar a versão 4.
Na versão 4,
eu não tenho mais um str, eu tenho um pStr.
Um ponteiro str.
Eu tô criando aqui, então, um ponteiro de char
que, como vocês podem intuir,
vai servir pra eu criar
um vetor dinamicamente
com o tamanho adaptado pro que eu preciso.
Se você olhar aqui, agora, a construtora MinhaString
que tá aqui.
Ela recebe o parâmetro que é um vetor de caracteres...
O comprimento desse parâmetro, esse vetorzinho de caracteres
e armazena no atributo tamanho.
E aí, agora, a construtora não tem mais código explicitamente,
o que ela tá fazendo é chamando uma função.
Isso significa que eu tô pegando o parâmetro s
e repassando pra essa função aqui, setString.
Significando que o código que antes estava aqui
ou que estaria aqui, em todo caso,
provavelmente foi terceirizado pra essa função.
E por que que eu não dei o código direto aqui
e terceirizei pra uma função?
Porque provavelmente esse código que tá aqui, que estaria aqui,
eu vou precisar em outro momento do código também,
então, pra evitar repetição de momentos de código,
blocos de código,
eu tô usando esse estratagema aqui.
Novamente, por que que o código não tá aqui?
Porque provavelmente eu vou precisar desse código que estaria aqui
em outras situações, em outros momentos
dos métodos aqui da classe corrente.
Assim sendo, eu usei essa estratégia
de colocar o código num lugar a parte
e poder chamá-lo aqui
e em outros lugares que sejam pertinentes.
Como eu tô fazendo o tratamento no tocante a s, eu tô repassando s aqui.
Cadê o setString?
setString tá definido lá, na classe.
Veja que ele também usa const char, tal qual a construtora.
Até por isso que eu consigo repassar o parâmetro.
Vejam que o setString é privado isso significa que
ele não vai ser acessado de fora apenas pelas
eventualmente, pelas funções pertinentes
e públicas aqui de MinhaString.
É o caso aqui da construtora
que é uma função pública
e tá utilizando setString repassando o parâmetro s.
Tá aqui a implementação do setString.
O que o setString faz?
Ele vai alocar caracteres.
Como é que eu sei que são caracteres e não um caractere?
Por causa do colchetes.
Então, aqui eu tô alocando um vetor de caracteres, eu tô alocando um conjunto de caracteres
e não um caractere só.
Isso é fundamental.
E quantos caracteres são?
É o tamanho do vetor. O tamanho +1.
Por que +1, professor?
Porque tem o \0.
O strlen ele ignora o \0 na contagem de caracteres,
ele conta os caracteres úteis
não conta, lá, o \0 que é o caractere de finalização de vetor de caracteres.
Conforme conta o conhecimento de linguagem C que vocês têm prévio.
Por isso que aqui, se eu tiver um vetor de 1000 caracteres,
na prática, eu vou precisar de 1001
pra comportar também o \0 que é o caractere de finalização.
tamanho +1.
Aqui eu tô usando o new char, tá.
Se fosse um malloc, memory allocation, da linguagem C
que também funciona em C++, mas em C++ a gente usa
largamente mais new do que malloc, tá,
mas só pra vocês saberem que é factível. Seria assim.
Só a fins de curiosidade isso aqui, tá. Por isso que tá comentado.
Aqui, é a alocação, então, de alocar um conjunto de caracteres.
Pessoal, se eu aloquei
um conjunto de caracteres e não um caractere,
se eu aloquei um conjunto de objetos e não um objeto,
o delete do ponteiro pertinente
é através da menção do colchetes.
Inclusive, isso já foi discutido
na exposição anterior,
Grupo de Slide 7 parte B,
lá, a hora que eu fala de arquivos binários em algum momento a gente utilizou
alocação de uma coletividade de objetos e não de um objeto
por meio de vetores.
Então, tá aqui.
Eu aloquei, no caso posto, um conjunto de caracteres,
por isso que eu tô dizendo que eu tô deletando um conjunto de caracteres
a partir de pStr que aponta pro início dessa coleção.
Agora que eu tenho um vetor
que comporta a quantidade de caracteres que foi passada por parâmetro
eu posso, então,
pegar esse parâmetro e copiar pra pStr
e tudo resolvido.
Não é isso?
Aqui, o que aconteceu? Tá.
Eu pude, gente
melhorar minha... meu objeto de gerenciamento
de vetores de caractere
pra ele alocar a quantidade apropriada de memória
pra gerenciar o que tá vindo por parâmetro pra ser gerenciado
pelo objeto em questão.
Isso significa que agora, por exemplo, ó.
Nesse caso aqui, ó.
"Minha primeira string soh minha."
Tem quantos caracteres aqui?
Vamos chutar 30.
30 caracteres.
Isso significa que eu vou ter, então, um vetor com 31 posições,
porque tem o \0.
Em vez de utilizar 300, como tava antes, lá, fixado.
Desperdiçando processamento, inclusive.
Bom, processamento também, mas memória principalmente.
Positivo?
Então, tá aqui.
E aí, eu vou imprimir aqui um vetor com o tamanho bem justo.