Capítulo 06 — Tipos: nem toda informação é igual
Série: Do zero ao jogo comercial com C++ e SFML
int, float, double, bool, char e std::string: começando a representar corretamente os diferentes dados do nosso jogo
No capítulo anterior nosso programa finalmente começou a lembrar informações.
Criamos variáveis como:
int playerHealth = 100;
int playerCoins = 25;
int playerLevel = 1;
E conseguimos alterar esses valores durante a execução:
playerHealth = playerHealth - 20;
playerCoins = playerCoins + 10;
playerLevel = 2;
Nosso pequeno programa passou a possuir estado.
Mas existe um problema.
Até agora todas as informações que armazenamos eram números inteiros.
Um jogo real não trabalha apenas com números como:
100
25
1
Também poderemos precisar representar:
4.5
"Alex"
true
'A'
Observe:
Health → 100
Speed → 4.5
Player Name → "Alex"
Is Alive → true
Grade → 'A'
Todas essas coisas são informações.
Mas elas claramente não representam a mesma espécie de informação.
E isso nos leva ao problema deste capítulo:
Como C++ sabe que tipo de informação estamos tentando armazenar?
A resposta começa com o sistema de tipos.
1. O problema aparece em nosso próprio código
Vamos imaginar que queremos expandir o estado inicial do jogador.
Já possuímos:
int playerHealth = 100;
int playerCoins = 25;
int playerLevel = 1;
Agora queremos adicionar a velocidade:
Player Speed: 4.5
Poderíamos tentar:
int playerSpeed = 4.5;
Mas existe algo estranho.
Declaramos:
int
e fornecemos:
4.5
Até agora utilizamos int com valores como:
100
25
1
Todos inteiros.
4.5 possui uma parte decimal.
Será que int é realmente o tipo adequado?
Antes de responder, precisamos entender melhor o que significa aquele:
int
que já estamos utilizando desde o capítulo anterior.
2. O tipo faz parte da declaração
Observe novamente:
int playerHealth = 100;
Podemos decompor:
int playerHealth = 100;
│ │ │
│ │ └── valor inicial
│ │
│ └── nome da variável
│
└── tipo
No capítulo anterior nossa atenção estava principalmente em:
playerHealth
Agora nosso foco será:
int
Quando escrevemos:
int playerHealth = 100;
estamos informando que playerHealth possui um determinado tipo.
Esse tipo ajuda C++ a determinar como aquela informação pode ser representada e quais operações fazem sentido sobre ela.
3. O que é um tipo?
Para nosso estágio atual, podemos utilizar esta definição:
Um tipo descreve a natureza de um dado e ajuda a determinar como ele pode ser representado e manipulado pelo programa.
Por exemplo:
int playerHealth = 100;
indica que estamos trabalhando com um número inteiro.
Enquanto:
bool isAlive = true;
representa uma informação lógica.
E:
std::string playerName = "Alex";
representa texto.
Então começamos a ter:
DADO TIPO
100 inteiro
4.5 decimal
"Alex" texto
true lógico
'A' caractere
Nem toda informação é igual.
Por isso não devemos tentar representar tudo da mesma maneira.
4. Tipos existem antes do nosso jogo
É importante perceber uma coisa.
Quando escrevemos:
int playerHealth = 100;
o conceito:
Health
é do nosso jogo.
Mas:
int
pertence à linguagem C++.
Nosso domínio diz:
precisamos representar a vida do jogador.
C++ nos oferece ferramentas para representar essa informação.
Neste caso escolhemos:
int
porque estamos trabalhando inicialmente com valores inteiros como:
100
80
50
0
Essa relação entre problema do domínio e representação técnica aparecerá muitas vezes durante a série.
5. int — números inteiros
Já conhecemos nosso primeiro tipo:
int
Ele é utilizado para representar números inteiros dentro da faixa suportada pela implementação.
Exemplos:
int playerHealth = 100;
int playerCoins = 25;
int playerLevel = 1;
Também podemos representar valores negativos:
int temperature = -5;
ou zero:
int playerCoins = 0;
Exemplos conceituais:
-100
-10
0
1
25
100
999
Não possuem parte decimal.
6. Onde int faz sentido em nosso jogo?
Podemos imaginar vários casos:
int playerHealth = 100;
int playerCoins = 25;
int playerLevel = 1;
int currentDay = 1;
int inventorySlots = 20;
Essas informações são naturalmente representáveis, neste estágio, como valores inteiros.
Por exemplo:
Level 1
Level 2
Level 3
faz sentido.
Já:
Level 2.438
provavelmente não faria sentido para o sistema de níveis que estamos imaginando.
O tipo escolhido deve refletir a informação que queremos representar.
7. Mas nem todo número é inteiro
Imagine a velocidade do jogador:
4.5
Agora temos uma parte decimal.
Poderíamos precisar de:
3.5
4.25
7.75
Nesse caso precisamos de um tipo capaz de representar valores de ponto flutuante.
Um dos tipos disponíveis em C++ é:
float
8. float — números de ponto flutuante
Podemos escrever:
float playerSpeed = 4.5f;
Observe o:
f
depois do valor:
4.5f
Esse sufixo indica que o literal deve ser tratado como float.
Então:
float playerSpeed = 4.5f;
pode ser lido inicialmente como:
crie uma variável chamada
playerSpeed, do tipofloat, inicializada com4.5.
9. Nosso primeiro float
Vamos testar:
#include <iostream>
int main()
{
float playerSpeed = 4.5f;
std::cout << "Player Speed: " << playerSpeed << std::endl;
return 0;
}
Faça nosso ciclo habitual:
Save
↓
Build
↓
Run
Resultado esperado:
Player Speed: 4.5
Agora nosso programa representa um valor que possui parte decimal.
10. Por que existe o f?
Observe:
float playerSpeed = 4.5f;
Poderíamos nos perguntar por que não simplesmente:
float playerSpeed = 4.5;
Em C++, um literal como:
4.5
é, por padrão, um literal de outro tipo de ponto flutuante:
double
Enquanto:
4.5f
é um literal float.
Ainda não precisamos entrar profundamente em conversões.
Por enquanto, vamos manter uma correspondência clara:
float playerSpeed = 4.5f;
Tipo:
float
Literal:
4.5f
Isso torna nossa intenção explícita.
11. Então existe também double
Sim.
Outro tipo importante para números de ponto flutuante é:
double
Por exemplo:
double elapsedTime = 12.75;
Agora não utilizamos:
f
porque:
12.75
já é um literal double por padrão.
Temos:
float
double
como dois tipos distintos para valores de ponto flutuante.
12. float e double não são simplesmente "a mesma coisa"
Uma simplificação comum seria dizer:
float → decimal
double → decimal maior
Mas isso esconde detalhes importantes.
Eles possuem características diferentes relacionadas a:
precisão;
faixa de valores;
representação;
armazenamento;
operações;
hardware;
conversões.
Ainda não precisamos aprofundar tudo isso.
O importante hoje é:
floatedoublesão tipos diferentes de ponto flutuante e não devem ser tratados como sinônimos.
Quando precisão e representação numérica se tornarem problemas reais, voltaremos a esse assunto.
13. Um cuidado: números decimais não são exatos como imaginamos
Existe outro ponto importante.
Valores de ponto flutuante são representados em formato binário e muitos números decimais não podem ser representados exatamente.
Isso significa que algo aparentemente simples como:
0.1
pode não possuir representação binária exata.
Ainda não vamos mergulhar em IEEE 754.
Mas já devemos guardar esta regra:
não devemos presumir que todo número de ponto flutuante representa exatamente o decimal que escrevemos.
Esse detalhe será importante futuramente em:
física;
movimento;
tempo;
comparação;
economia;
cálculos.
14. Dinheiro merece cuidado especial
Imagine:
float itemPrice = 19.99f;
Parece natural.
Mas sistemas monetários reais exigem muito cuidado com precisão.
Quando construirmos a economia do nosso jogo, discutiremos qual representação faz sentido para dinheiro.
Não vamos decidir isso agora.
Por enquanto:
int playerCoins = 25;
é suficiente porque estamos representando uma quantidade inteira de moedas.
Essa é uma decisão muito mais simples.
15. Agora precisamos de texto
Nosso jogador também precisa de um nome.
Imagine:
Player Name: Alex
Não podemos representar isso adequadamente com:
int
nem:
float
Precisamos representar uma sequência de caracteres.
Em nosso projeto utilizaremos:
std::string
16. std::string — texto
Podemos escrever:
std::string playerName = "Alex";
Mas agora precisamos incluir:
#include <string>
Nosso programa:
#include <iostream>
#include <string>
int main()
{
std::string playerName = "Alex";
std::cout << "Player Name: " << playerName << std::endl;
return 0;
}
Resultado:
Player Name: Alex
Agora nosso estado pode conter texto.
17. Por que std::string possui std::?
No Capítulo 02 vimos algo semelhante:
std::cout
e:
std::endl
Agora encontramos:
std::string
Novamente:
std
está relacionado à biblioteca padrão do C++.
E:
::
é o operador de resolução de escopo.
Portanto:
std::string
indica o string que estamos utilizando dentro do namespace std.
Ainda não criaremos nosso próprio namespace.
18. Aspas duplas importam
Observe:
std::string playerName = "Alex";
O texto está entre:
" "
aspas duplas.
Por exemplo:
std::string farmName = "Green Valley";
representa uma string.
Isso será diferente do próximo tipo que veremos.
19. char — um caractere
Imagine que nosso jogador tenha uma classificação:
Grade: A
Queremos armazenar apenas um caractere.
Podemos utilizar:
char playerGrade = 'A';
Observe:
'A'
utiliza aspas simples.
Enquanto:
std::string playerName = "Alex";
utiliza aspas duplas.
Temos:
'A' → char
"Alex" → string
Essa diferença importa.
20. Testando char
#include <iostream>
int main()
{
char playerGrade = 'A';
std::cout << "Player Grade: " << playerGrade << std::endl;
return 0;
}
Resultado:
Player Grade: A
O tipo:
char
é utilizado para representar um caractere.
Mas cuidado com uma simplificação excessiva.
Texto, caracteres e Unicode são assuntos muito maiores do que este pequeno exemplo.
Quando internacionalização e encoding se tornarem relevantes, precisaremos aprofundar o assunto.
21. Um jogo também precisa representar verdadeiro ou falso
Imagine a pergunta:
O jogador está vivo?
Existem duas respostas lógicas:
sim
não
Podemos representar isso com:
bool
Exemplo:
bool isAlive = true;
O tipo:
bool
representa valores booleanos.
Os dois valores fundamentais são:
true
false
22. bool — informação lógica
Exemplo:
bool isAlive = true;
Podemos ter:
bool isAlive = true;
bool isGamePaused = false;
bool hasKey = false;
Observe novamente nossa convenção camelCase.
Para booleanos, nomes que expressam uma condição costumam tornar o código bastante legível:
isAlive
isGamePaused
hasKey
Eles sugerem perguntas:
Is alive?
Is game paused?
Has key?
23. Exibindo um bool
Experimente:
#include <iostream>
int main()
{
bool isAlive = true;
std::cout << "Is Alive: " << isAlive << std::endl;
return 0;
}
Você provavelmente verá:
Is Alive: 1
em vez de:
Is Alive: true
Por quê?
Por padrão, std::cout pode representar valores booleanos numericamente:
false → 0
true → 1
Isso não significa que nossa variável deixou de ser bool.
Ela continua sendo:
bool isAlive = true;
24. Mostrando true e false
Podemos pedir ao stream para exibir booleanos textualmente utilizando:
std::boolalpha
Por exemplo:
#include <iostream>
int main()
{
bool isAlive = true;
std::cout << std::boolalpha;
std::cout << "Is Alive: " << isAlive << std::endl;
return 0;
}
Resultado:
Is Alive: true
Ainda não precisamos aprofundar manipuladores de stream.
Estamos apenas utilizando um recurso conveniente para visualizar melhor nosso estado.
25. Nosso estado agora possui tipos diferentes
Chegamos a:
int playerHealth = 100;
float playerSpeed = 4.5f;
std::string playerName = "Alex";
bool isAlive = true;
char playerGrade = 'A';
Agora observe:
Informação Tipo Valor
Health int 100
Speed float 4.5f
Name std::string "Alex"
Is Alive bool true
Grade char 'A'
Nosso programa começa a representar informações diferentes utilizando tipos adequados a cada situação.
26. Vamos juntar tudo
Nosso main.cpp pode evoluir para:
#include <iostream>
#include <string>
int main()
{
int playerHealth = 100;
float playerSpeed = 4.5f;
std::string playerName = "Alex";
bool isAlive = true;
char playerGrade = 'A';
std::cout << std::boolalpha;
std::cout << "=== PLAYER STATE ===" << std::endl;
std::cout << "Name: " << playerName << std::endl;
std::cout << "Health: " << playerHealth << std::endl;
std::cout << "Speed: " << playerSpeed << std::endl;
std::cout << "Is Alive: " << isAlive << std::endl;
std::cout << "Grade: " << playerGrade << std::endl;
return 0;
}
Faça:
Save
↓
Build
↓
Run
Resultado esperado:
=== PLAYER STATE ===
Name: Alex
Health: 100
Speed: 4.5
Is Alive: true
Grade: A
Nosso pequeno estado está ficando mais interessante.
27. Mas como escolher o tipo?
Essa é uma pergunta melhor do que:
"Qual tipo devo decorar?"
Antes de escolher, pergunte:
Que informação estou tentando representar?
Por exemplo:
Quantidade de moedas
↓
número inteiro
↓
int
Enquanto:
Velocidade
↓
pode possuir parte fracionária
↓
float
Ou:
Nome do jogador
↓
texto
↓
std::string
Ou:
Jogador está vivo?
↓
verdadeiro/falso
↓
bool
O tipo deve partir do significado da informação.
28. Não escolha tipos apenas pelo tamanho aparente do valor
Imagine:
1
Esse valor poderia representar:
playerLevel
e então:
int playerLevel = 1;
Mas também poderíamos estar tentando representar uma condição lógica usando 1.
Mesmo que tecnicamente existam conversões possíveis, isso não significa que seja a representação mais clara.
Se a informação é:
o jogo está pausado?
prefira expressar essa intenção:
bool isGamePaused = false;
em vez de transformar tudo em números sem significado claro.
29. Tipos também restringem operações
Uma vantagem importante do sistema de tipos é impedir ou sinalizar determinadas combinações inadequadas.
Considere:
int playerHealth = 100;
Depois:
playerHealth = "Alex";
Isso não representa uma operação coerente.
Estamos tentando colocar texto em uma variável declarada para representar um inteiro.
Faça esse erro propositalmente.
30. Erro proposital — tipo incompatível
Use:
#include <iostream>
int main()
{
int playerHealth = 100;
playerHealth = "Alex";
return 0;
}
Faça Build.
Não se preocupe em decorar a mensagem completa do compilador.
Use nossa metodologia:
ERRO
↓
LER A MENSAGEM
↓
LOCALIZAR A LINHA
↓
IDENTIFICAR OS TIPOS
↓
COMPARAR A INTENÇÃO
↓
CORRIGIR
↓
VALIDAR
Temos:
playerHealth → int
"Alex" → texto
Essas informações não correspondem.
31. O compilador está nos ajudando
À primeira vista, um erro pode parecer inconveniente.
Mas imagine se C++ simplesmente aceitasse silenciosamente qualquer coisa:
playerHealth = "Alex";
O que significaria a vida do jogador ser:
Alex
para nossa regra?
O sistema de tipos ajuda a impedir determinadas inconsistências antes mesmo da execução.
Isso é extremamente valioso.
32. Static Typing
C++ é uma linguagem estaticamente tipada.
Isso significa, de maneira inicial, que os tipos das expressões e variáveis são determinados/verificados no contexto da compilação.
Quando escrevemos:
int playerHealth = 100;
playerHealth possui tipo:
int
Não estamos dizendo:
"agora é inteiro, depois talvez vire texto."
O tipo faz parte da identidade estática daquela variável.
Esse assunto será aprofundado ao longo da série.
33. O tipo não muda porque o valor mudou
Considere:
int playerHealth = 100;
playerHealth = 80;
playerHealth = 50;
playerHealth = 0;
Os valores mudaram:
100
↓
80
↓
50
↓
0
Mas o tipo permaneceu:
int
Portanto:
valor
e:
tipo
são conceitos diferentes.
34. Tipo e valor
Podemos pensar:
Variável: playerHealth
Tipo:
int
Valor atual:
100
Depois:
Variável: playerHealth
Tipo:
int
Valor atual:
80
O valor mudou.
O tipo não.
Essa distinção será fundamental quando começarmos a estudar funções, parâmetros, classes, templates e APIs.
35. Tipos ocupam memória
Tipos também estão relacionados à forma como os dados são representados na memória.
Talvez você já tenha ouvido afirmações como:
int → 4 bytes
float → 4 bytes
double → 8 bytes
Esses tamanhos são comuns em nosso ambiente, mas não devemos transformar isso em uma regra universal sem contexto.
C++ estabelece requisitos e a implementação/plataforma influencia detalhes.
Vamos medir nosso próprio ambiente.
36. Conhecendo sizeof
C++ possui o operador:
sizeof
Podemos utilizá-lo para consultar o tamanho, em bytes, de um tipo ou objeto.
Experimente:
#include <iostream>
int main()
{
std::cout << "int: " << sizeof(int) << " bytes" << std::endl;
std::cout << "float: " << sizeof(float) << " bytes" << std::endl;
std::cout << "double: " << sizeof(double) << " bytes" << std::endl;
std::cout << "bool: " << sizeof(bool) << " bytes" << std::endl;
std::cout << "char: " << sizeof(char) << " bytes" << std::endl;
return 0;
}
Execute no seu ambiente.
Em uma configuração comum com MSVC x64, você provavelmente observará valores semelhantes a:
int: 4 bytes
float: 4 bytes
double: 8 bytes
bool: 1 bytes
char: 1 bytes
Mas o objetivo do exercício não é decorar a tabela.
É perceber:
tipos possuem características concretas de representação.
37. Por que não incluímos std::string nessa comparação?
Podemos medir:
sizeof(std::string)
mas interpretar esse resultado exige cuidado.
Uma std::string é um objeto mais complexo do que simplesmente:
quantidade de caracteres = tamanho total do objeto
Sua implementação pode gerenciar armazenamento e possuir estratégias próprias.
Se imprimirmos apenas um número e dissermos:
"
std::stringocupa X bytes"
poderemos criar uma compreensão errada.
Quando estudarmos objetos, memória dinâmica e containers, poderemos interpretar isso melhor.
Por enquanto, não precisamos.
38. char possui uma característica especial
O padrão da linguagem define:
sizeof(char) == 1
Mas esse 1 representa uma unidade chamada byte no contexto de C++.
Isso não significa que devemos concluir imediatamente todas as características da arquitetura apenas a partir disso.
Memória em C++ é um assunto profundo.
Estamos apenas abrindo a porta.
39. Tipos possuem limites
Um int não consegue representar qualquer número imaginável.
Existe uma faixa finita de valores.
Da mesma forma, float e double possuem limitações de faixa e precisão.
Mais tarde veremos ferramentas como:
std::numeric_limits
para investigar essas propriedades de maneira adequada.
Não precisamos fazer isso hoje.
Nosso objetivo ainda é reconhecer tipos e escolher representações coerentes.
40. Overflow existe
Imagine um tipo com determinada faixa.
Se tentarmos ultrapassar seus limites, poderemos ter problemas.
No futuro discutiremos:
overflow;
underflow;
signed;
unsigned;
conversões;
comportamento definido;
comportamento indefinido.
Esses assuntos merecem atenção própria.
Não vamos colocar tudo dentro deste capítulo.
41. E unsigned?
Talvez você já tenha visto:
unsigned int playerCoins = 25;
e pensado:
"Como moedas não podem ser negativas, então
unsigneddeve ser automaticamente melhor."
Essa conclusão é perigosa.
Tipos unsigned possuem implicações importantes em operações, comparações e conversões.
Não vamos utilizá-los apenas porque um valor conceitualmente não deveria ser negativo.
Quando precisarmos discutir signedness, faremos isso com exemplos concretos.
Por enquanto:
int playerCoins = 25;
continua adequado para nossa etapa didática.
42. E short, long e long long?
Eles existem.
Assim como várias outras possibilidades.
Mas listar tudo agora nos faria transformar este capítulo em:
"decore os tipos fundamentais do C++."
Esse não é nosso objetivo.
Hoje precisamos dos tipos que ajudam nosso pequeno jogo a avançar:
int
float
double
bool
char
std::string
Outros tipos aparecerão quando houver motivo real para utilizá-los.
43. std::string não é um tipo fundamental
Existe uma precisão técnica importante.
Tipos como:
int
float
double
bool
char
fazem parte dos tipos fundamentais da linguagem.
Já:
std::string
é uma classe fornecida pela biblioteca padrão.
Estamos apresentando todos juntos porque, do ponto de vista didático, precisamos representar diferentes informações do jogo.
Mas tecnicamente eles não pertencem exatamente à mesma categoria.
Essa distinção será muito mais clara quando estudarmos classes.
44. Um primeiro mapa dos tipos que usamos
Podemos organizar:
C++ FUNDAMENTAL TYPES
│
├── int
├── float
├── double
├── bool
└── char
STANDARD LIBRARY
│
└── std::string
Essa pequena separação evita um erro conceitual comum.
45. auto existe, mas não precisamos dele ainda
C++ também permite escrever coisas como:
auto playerHealth = 100;
e deixar o compilador deduzir o tipo.
auto é extremamente útil em C++ moderno.
Mas utilizá-lo agora esconderia justamente aquilo que estamos tentando aprender.
Neste momento queremos enxergar:
int playerHealth = 100;
float playerSpeed = 4.5f;
bool isAlive = true;
Quando entendermos melhor tipos e dedução, auto aparecerá naturalmente.
46. Não existe prêmio por usar o menor tipo possível
Talvez alguém pense:
"Se o Level só vai até 100, preciso encontrar o menor tipo possível para economizar memória."
Isso é otimização prematura.
Nosso programa possui cinco variáveis.
Não temos evidência de problema de memória.
Mais tarde poderemos ter:
milhões de elementos
grandes buffers
estruturas massivas
dados de GPU
cache-sensitive systems
e o tamanho dos tipos poderá importar enormemente.
Mas primeiro:
MEASURE
↓
IDENTIFY
↓
OPTIMIZE
↓
MEASURE AGAIN
Não sacrificaremos clareza sem necessidade.
47. O contexto do jogo determina a representação
Considere:
playerHealth
Hoje:
int playerHealth = 100;
parece perfeito.
Mas imagine um jogo em que a vida pudesse ser:
99.75
Talvez outro tipo fosse necessário.
Ou imagine que Health fosse representado por:
Healthy
Injured
Critical
Dead
Talvez um modelo completamente diferente fosse mais adequado.
Portanto:
o tipo não deve ser escolhido porque "Health sempre é int".
Ele deve ser escolhido de acordo com a regra do sistema que estamos construindo.
48. Tipos são parte da modelagem
Esse ponto começa a aproximar fundamentos de programação de Engenharia de Software.
Quando escolhemos:
bool isAlive;
estamos dizendo algo sobre nosso modelo:
naquele contexto, essa condição possui dois estados relevantes.
Quando escolhemos:
int playerLevel;
estamos dizendo:
nosso nível será representado numericamente como inteiro.
Essas escolhas carregam significado.
Tipos não são apenas detalhes sintáticos.
49. Nosso pequeno estado atualizado
Vamos utilizar:
#include <iostream>
#include <string>
int main()
{
std::string playerName = "Alex";
int playerHealth = 100;
int playerCoins = 25;
int playerLevel = 1;
float playerSpeed = 4.5f;
bool isAlive = true;
char playerGrade = 'A';
std::cout << std::boolalpha;
std::cout << "=== PLAYER STATE ===" << std::endl;
std::cout << "Name: " << playerName << std::endl;
std::cout << "Health: " << playerHealth << std::endl;
std::cout << "Coins: " << playerCoins << std::endl;
std::cout << "Level: " << playerLevel << std::endl;
std::cout << "Speed: " << playerSpeed << std::endl;
std::cout << "Is Alive: " << isAlive << std::endl;
std::cout << "Grade: " << playerGrade << std::endl;
return 0;
}
Resultado:
=== PLAYER STATE ===
Name: Alex
Health: 100
Coins: 25
Level: 1
Speed: 4.5
Is Alive: true
Grade: A
Nosso jogo ainda é apenas console.
Mas o modelo de informações está crescendo.
50. Microdesafio — escolha o tipo
Sem olhar a resposta, escolha um tipo inicial adequado para:
playerHealth
playerSpeed
playerName
isGamePaused
playerGrade
playerCoins
Uma possível resposta:
int playerHealth = 100;
float playerSpeed = 4.5f;
std::string playerName = "Alex";
bool isGamePaused = false;
char playerGrade = 'A';
int playerCoins = 25;
Mais importante que acertar mecanicamente é conseguir explicar por que escolheu cada tipo.
51. Microdesafio — nossa futura fazenda
Agora imagine informações da fazenda:
Farm Name
Current Day
Temperature
Is Raining
Season Grade
Money
Escolha representações iniciais.
Não precisamos de um sistema de fazenda ainda.
É apenas um exercício de modelagem.
Uma possível solução:
std::string farmName = "Green Valley";
int currentDay = 1;
float temperature = 24.5f;
bool isRaining = false;
char seasonGrade = 'A';
int money = 500;
Mais tarde descobriremos que alguns desses tipos talvez não sejam os melhores para o sistema definitivo.
Isso é normal.
Nosso modelo evoluirá com os requisitos.
52. Erro proposital — aspas
Experimente:
char playerGrade = "A";
Faça Build.
Depois compare com:
char playerGrade = 'A';
Agora faça o inverso:
std::string playerName = 'Alex';
Isso também está errado — e um literal de caractere não representa uma string dessa maneira.
Compare:
char playerGrade = 'A';
std::string playerName = "Alex";
Guarde visualmente:
'A' char
"Alex" std::string
53. Erro proposital — esquecendo <string>
Remova:
#include <string>
mantendo:
std::string playerName = "Alex";
Dependendo dos headers e da implementação, código pode aparentemente continuar compilando por dependências indiretas em certos contextos.
Mas não devemos depender disso.
Se utilizamos diretamente:
std::string
devemos incluir seu header correspondente:
#include <string>
Esse princípio será importante:
inclua diretamente aquilo de que seu arquivo depende diretamente.
Não dependa acidentalmente de outro header ter incluído algo por você.
54. Não vamos criar abstrações ainda
Temos:
std::string playerName;
int playerHealth;
int playerCoins;
int playerLevel;
float playerSpeed;
bool isAlive;
char playerGrade;
Talvez pareça tentador criar:
class Player
{
};
Mas ainda estamos aprendendo tipos básicos e variáveis.
Não existe necessidade didática de introduzir classes neste momento.
Nosso main.cpp continua suficiente.
A classe Player aparecerá quando compreendermos o problema que ela resolverá.
55. Nossa estrutura continua simples
CppGameEngine/
└── main.cpp
Nenhuma pasta nova.
Nenhuma camada nova.
Nenhum Manager.
Nenhum Service.
Nenhum Component.
Estamos aumentando conhecimento, não complexidade arquitetural.
56. Validando o capítulo
Antes de registrar a mudança:
1. Save
Ctrl + S
2. Build
Ctrl + Shift + B
Confirme:
Build succeeded
3. Run
Ctrl + F5
4. Verifique
Name: Alex
Health: 100
Coins: 25
Level: 1
Speed: 4.5
Is Alive: true
Grade: A
Não basta compilar.
Observe se cada informação exibida corresponde ao estado declarado.
57. Nosso commit
Agora existe uma evolução real no código.
Primeiro:
git status
Depois adicione o arquivo real:
git add CppGameEngine/main.cpp
Ajuste o caminho caso sua estrutura local seja diferente.
Commit:
git commit -m "feat: add typed player state data"
O commit descreve a mudança:
nosso estado do jogador agora utiliza diferentes tipos de dados.
58. Atualizando o CHANGELOG
Em [Unreleased], podemos adicionar:
### Added
- Added typed player state data.
- Added player name, speed, alive state, and grade.
- Added examples using `int`, `float`, `bool`, `char`, and `std::string`.
Depois:
git add CHANGELOG.md
git commit -m "docs: update changelog with typed player state"
Não precisamos atualizar o ROADMAP técnico por cada novo fundamento da linguagem.
59. O que aprendemos?
No capítulo anterior descobrimos que variáveis permitem armazenar informações.
Hoje descobrimos que essas informações possuem tipos.
Começamos com:
int playerHealth = 100;
e evoluímos para:
std::string playerName = "Alex";
int playerHealth = 100;
float playerSpeed = 4.5f;
bool isAlive = true;
char playerGrade = 'A';
Conhecemos:
int
float
double
bool
char
std::string
e aprendemos que:
intrepresenta valores inteiros;floatedoublerepresentam valores de ponto flutuante com características diferentes;boolrepresenta condições lógicas;charrepresenta um caractere;std::stringrepresenta texto e pertence à biblioteca padrão;o tipo e o valor são conceitos diferentes;
mudar o valor não muda automaticamente o tipo;
tipos possuem características de representação e armazenamento;
sizeofpermite investigar tamanho;escolher um tipo é também uma decisão de modelagem.
Mais importante:
não escolhemos um tipo porque decoramos uma tabela. Escolhemos um tipo porque entendemos a informação que precisamos representar.
Conclusão
Nosso pequeno programa começou com uma frase:
Game Engineering com C++ começou!
Depois ganhou variáveis.
Agora essas variáveis começaram a representar diferentes naturezas de informação:
PLAYER STATE
Name → "Alex"
Health → 100
Coins → 25
Level → 1
Speed → 4.5
Is Alive → true
Grade → 'A'
Estamos lentamente transformando um programa de console em um pequeno modelo de estado de jogo.
Ainda não temos gráficos.
Ainda não temos SFML.
Ainda não temos Player.
Ainda não temos Engine.
E isso é exatamente o que queremos.
Cada ferramenta aparecerá quando existir um problema que justifique sua presença.
Agora surge outro problema.
Imagine que nosso jogo tenha uma regra:
Maximum Health = 100
O valor:
playerHealth
pode mudar:
100 → 80 → 50
Mas talvez:
MAX_PLAYER_HEALTH
não devesse mudar durante a execução.
Até agora todas as nossas informações foram tratadas como variáveis mutáveis.
Precisamos aprender a expressar outra intenção:
este valor não deve ser alterado.
E é exatamente daí que nasce nosso próximo assunto.
Próximo capítulo
Capítulo 07 — Constantes: valores que não deveriam mudar
Vamos partir de um problema concreto:
int maxPlayerHealth = 100;
Tecnicamente, nada nessa declaração impede que alguém faça depois:
maxPlayerHealth = 500;
Mas e se isso violar a intenção do nosso programa?
Vamos descobrir como C++ permite expressar que determinados valores devem permanecer constantes e começar a utilizar nossa convenção:
UPPERCASE
com nomes em inglês, como:
MAX_PLAYER_HEALTH
Também veremos por que constantes não existem apenas para "evitar alterações acidentais".
Elas tornam intenções e regras do código mais explícitas.
Nosso próximo passo será transformar:
um valor que por acaso não mudou
em:
um valor que o código declara que não deve mudar
E isso nos levará a uma das ideias mais importantes do C++ moderno: const correctness, começando pela sua forma mais simples.
Comentários
Ainda não há comentários. Sê o primeiro a comentar!