Aeca

Capítulo 06 — Tipos: nem toda informação é igual

Série: Do zero ao jogo comercial com C++ e SFML

Capítulo 06 — Tipos: nem toda informação é igual
aeca

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 tipo float, inicializada com 4.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 é:

float e double sã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::string ocupa 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 unsigned deve 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:

  • int representa valores inteiros;

  • float e double representam valores de ponto flutuante com características diferentes;

  • bool representa condições lógicas;

  • char representa um caractere;

  • std::string representa 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;

  • sizeof permite 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.

Subscreve "Aeca" para receber atualizações diretamente na tua caixa de entrada
aeca

Subscreve aeca para reagir

Subscrever

Comentários

Ainda não há comentários. Sê o primeiro a comentar!

Subscreve Aeca para receber atualizações diretamente na tua caixa de entrada