# Fundamentos de C++

Tipos estáticos, declarações, entrada e saída, referências, const e strings, com compilação em g++.

Página: https://resumos.rgo.pt/cadeiras/p/cpp-fundamentos/

Em [Primeiros programas](https://resumos.rgo.pt/cadeiras/fp/primeiros-programas/) escrevias `x = 5` e Python decidia sozinho que `x` era um inteiro. Em C++ tens de dizer qual é o tipo antes de usar a variável, e esse tipo nunca mais muda. Isto parece burocracia, mas é uma troca: o compilador verifica os tipos antes de o programa correr e apanha uma classe inteira de erros ainda no editor.

## Declarar variáveis com tipos fixos

Cada variável precisa de um **tipo** e de um **nome** antes do primeiro uso. Os tipos básicos que vais usar são `int` (inteiros), `double` (reais), `char` (caracteres) e `bool` (verdadeiro ou falso):

```
#include <iostream>

int main() {
    int idade = 19;
    double media = 14.5;
    char inicial = 'A';
    bool aprovado = true;
    std::cout << idade << " " << media << " " << inicial << " " << aprovado << "\n";
}
```

Isto escreve `19 14.5 A 1`. Repara que um `bool` sai como `1` (verdadeiro) ou `0` (falso) por omissão. E repara também que cada linha termina com ponto e vírgula: esquecer o ponto e vírgula é o erro de compilação mais comum das primeiras semanas, e a mensagem do compilador aponta quase sempre para a linha seguinte.

O tipo fixa o que a variável pode guardar. Se escreveres `int x = 3.7;`, o compilador aceita mas corta a parte decimal e guarda `3`. Em Python o valor mudava de tipo sem avisar; em C++ a variável manda, não o valor.

## Entrada e saída com iostream

A biblioteca `iostream` trata da entrada e da saída. O operador `<<` envia valores para o ecrã (`std::cout`) e o operador `>>` lê valores do teclado (`std::cin`):

```
#include <iostream>

int main() {
    int n;
    std::cout << "Diz um numero: ";
    std::cin >> n;
    std::cout << "O dobro e " << 2 * n << "\n";
}
```

Se escreveres `7` na entrada, isto mostra `Diz um numero: O dobro e 14`. A direção dos parênteses angulares ajuda a memorizar: os dados fluem na direção para onde as setas apontam, do programa para o ecrã com `<<` e do teclado para a variável com `>>`.

O prefixo `std::` indica que `cout` e `cin` pertencem à biblioteca padrão. Vais ver programas que escrevem `using namespace std;` para o omitir, mas nos teus primeiros programas prefere o prefixo explícito: deixa claro de onde vem cada nome.

## Compilar e correr com g++

C++ é uma linguagem **compilada**: escreves o código num ficheiro, o compilador traduz esse ficheiro para código máquina e só depois executas o resultado. Guarda o programa acima em `dobro.cpp` e corre estes dois comandos no terminal, na pasta do ficheiro:

```
g++ -Wall -std=c++17 -o dobro dobro.cpp
./dobro
```

O `g++` já vem instalado na maioria das distribuições Linux; se faltar, instala o pacote `g++` do teu gestor de pacotes.

```
g++ -Wall -std=c++17 -o dobro dobro.cpp
./dobro
```

No macOS, o comando `g++` é um atalho para o Clang, que aceita as mesmas opções para estes programas. Se o terminal responder `command not found`, instala as ferramentas com `xcode-select --install`.

```
g++ -Wall -std=c++17 -o dobro dobro.cpp
./dobro
```

No Windows, corre estes comandos dentro do WSL (Windows Subsystem for Linux) com uma distribuição Ubuntu, onde o `g++` se instala com `sudo apt install g++`. Evita compilar em pastas sincronizadas com o OneDrive, que costumam causar erros estranhos de escrita.

O primeiro comando compila: `-o dobro` escolhe o nome do executável, `-std=c++17` fixa a versão da linguagem e `-Wall` liga os avisos do compilador, que apanham erros subtis antes de correres o programa. Se a compilação não escrever nada, correu bem. O segundo comando executa o programa. Quando houver um erro, o `g++` indica o ficheiro, a linha e a razão; lê essa mensagem primeiro e só depois olhes para o código.

Compila com avisos desde o primeiro dia

O `-Wall` não é opcional para quem está a aprender. Muitos programas que “compilam bem” sem avisos escondem conversões de tipos ou variáveis não usadas que depois aparecem como resultados errados. Habituar-te a compilar sem avisos poupa horas de depuração.

## Projetos com CMake e CLion

Um ficheiro compila-se com um comando `g++`, mas um projeto com vários ficheiros precisa de uma receita de compilação. O **CMake** descreve essa receita num ficheiro `CMakeLists.txt`, e o **CLion** é o ambiente da cadeira que corre o CMake por ti. Um projeto mínimo com um só programa precisa disto:

```
cmake_minimum_required(VERSION 3.16)
project(notas)
set(CMAKE_CXX_STANDARD 17)
add_executable(notas main.cpp)
```

Cada linha diz uma coisa: versão mínima do CMake, nome do projeto, versão da linguagem e que executável construir a partir de que ficheiros. No CLion, crias um projeto novo, colas o teu código no `main.cpp` e carregas no martelo para compilar e no triângulo para correr: o ambiente escreve estes comandos por ti, mas é o mesmo `g++` por baixo. Quando o CLion se queixar de que não encontra o compilador, verifica nas definições o caminho da toolchain; no WSL, o CLion precisa da toolchain remota apontada à distribuição Ubuntu.

## Referências contra cópias

Quando passas uma variável a uma função em C++, a função recebe por omissão uma **cópia**: alterar o parâmetro dentro da função não toca na variável original. Para a função alterar o argumento, pede uma **referência** com `&`, que é um segundo nome para a mesma variável:

```
#include <iostream>

void por_copia(int x) { x = 0; }
void por_referencia(int& x) { x = 0; }

int main() {
    int a = 5;
    por_copia(a);
    std::cout << a << "\n";
    por_referencia(a);
    std::cout << a << "\n";
}
```

Isto escreve `5` e depois `0`. A primeira chamada alterou apenas a cópia, por isso `a` continuou `5`. A segunda chamada alterou a própria variável `a` através da referência. Esta distinção não existia em [Funções](https://resumos.rgo.pt/cadeiras/fp/funcoes/): em Python, passar uma lista já permitia alterá-la, e passar um número nunca permitia. Em C++, és tu que escolhes, parâmetro a parâmetro.

Usa referências também para evitar copiar valores grandes sem necessidade, como vais ver nas [strings](https://resumos.rgo.pt/cadeiras/p/cpp-fundamentos/#strings) e nos vetores da [STL](https://resumos.rgo.pt/cadeiras/p/templates-stl/).

[Vídeo: REFERENCES in C++, The Cherno](https://www.youtube.com/watch?v=IzoFn3dfsPA)

A miniatura vem do YouTube. O vídeo só carrega quando clicas. [Abrir no YouTube](https://www.youtube.com/watch?v=IzoFn3dfsPA)

## const: prometer que não muda

A palavra `const` declara que um valor não vai ser alterado. Usa-a em parâmetros que a função só lê, porque o compilador passa a impedir alterações acidentais:

```
#include <iostream>

int soma_tres(const int& x) {
    return x + 3;
}

int main() {
    int a = 5;
    std::cout << soma_tres(a) << " " << a << "\n";
}
```

Isto escreve `8 5`. O parâmetro é uma referência (sem cópia) mas é `const` (sem alterações): a função lê `x` e devolve `8`, e `a` continua `5`. Se tentasses escrever `x = 0;` dentro da função, o programa nem compilava. Mais tarde vais aplicar a mesma ideia aos métodos das tuas [classes](https://resumos.rgo.pt/cadeiras/p/classes-objetos/).

## Strings

Texto em C++ é a classe `std::string`, da biblioteca `<string>`. Ao contrário das strings de Python, que já conheces de [Tuplos e listas](https://resumos.rgo.pt/cadeiras/fp/tuplos-listas/), aqui precisas de incluir a biblioteca e de decidir como passar cada string às funções:

```
#include <iostream>
#include <string>

int comprimento_duplo(const std::string& s) {
    return 2 * s.size();
}

int main() {
    std::string nome;
    std::cout << "Como te chamas? ";
    std::cin >> nome;
    std::cout << "Ola, " << nome << "! O dobro do tamanho e "
              << comprimento_duplo(nome) << "\n";
}
```

Se escreveres `Ana` na entrada, isto mostra `Como te chamas? Ola, Ana! O dobro do tamanho e 6`. O método `size()` devolve o número de caracteres, tal como `len` em Python. Repara que `std::cin >> nome` lê apenas até ao primeiro espaço: `Ana Maria` ficaria só `Ana`. Para ler uma linha inteira usa `std::getline`, como na secção de ficheiros abaixo.

## Ler e escrever ficheiros

Teclado e ecrã chegam para os primeiros exercícios, mas os projetos leem e escrevem ficheiros. As classes `ifstream` (ler) e `ofstream` (escrever) trabalham como `cin` e `cout`, com `>>` e `<<`; a diferença é que abres um ficheiro pelo nome e o fechas no fim:

```
#include <fstream>
#include <iostream>
#include <string>

int main() {
    std::ofstream saida("notas.txt");
    saida << "Ana 15\nRui 12\n";
    saida.close();
    std::ifstream entrada("notas.txt");
    std::string nome;
    int nota;
    while (entrada >> nome >> nota) {
        std::cout << nome << ": " << nota << "\n";
    }
}
```

Isto escreve o ficheiro `notas.txt` com duas linhas e depois lê-o de volta, mostrando `Ana: 15` e `Rui: 12`. O ciclo `while (entrada >> nome >> nota)` funciona porque ler de um fluxo devolve o próprio fluxo, que em condição é falso quando a leitura falha, ou seja, no fim do ficheiro. Fecha sempre a escrita com `close()` antes de leres o mesmo ficheiro, para garantir que tudo foi gravado. Para ler uma linha inteira com espaços, troca `entrada >> nome` por `std::getline(entrada, nome)` e trata a linha depois.

## Exemplo completo: média de três notas

Vamos juntar declarações, leitura, uma função com referência constante e saída formatada. O programa lê três notas, calcula a média numa função e diz se o aluno passa (média maior ou igual a 10). Corre-o com a entrada `12 15 9`:

```cpp
#include <iostream>

double media(double a, double b, double c) {
    return (a + b + c) / 3.0;
}

int main() {
    double n1, n2, n3;
    std::cout << "Tres notas: ";
    std::cin >> n1 >> n2 >> n3;
    double m = media(n1, n2, n3);
    std::cout << "Media: " << m << "\n";
    if (m >= 10.0) {
        std::cout << "Aprovado\n";
    } else {
        std::cout << "Reprovado\n";
    }
}
```

Isto escreve:

```
Tres notas: Media: 12
Aprovado
```

Confere a conta: $(12 + 15 + 9) / 3 = 12$. O `3.0` em vez de `3` garante a divisão real, e a diferença entre os dois tipos de divisão resume-se assim:

![Tabela com duas linhas: 7 / 2 é divisão inteira e dá 3; 7 / 2.0 é divisão real e dá 3.5.](https://resumos.rgo.pt/cadeiras/p/cpp-fundamentos/figura-1.svg)

Com inteiros, C++ corta a parte decimal, e `36 / 3` até daria certo aqui, mas `35 / 3` daria `11` em vez de `11.67`. Sempre que quiseres resultado real, envolve pelo menos um operando real na divisão.

Os erros mais comuns do início

Esquecer o ponto e vírgula; esquecer o `#include` da biblioteca usada (`<string>` para strings); comparar strings com `==` funciona em C++ (ao contrário de arrays de C, que vais conhecer nos [apontadores](https://resumos.rgo.pt/cadeiras/p/apontadores-memoria/)); e declarar a variável sem tipo por hábito de Python. Quando o compilador reclamar, lê a primeira mensagem de erro e ignora as seguintes, que costumam ser consequências da primeira.

## Para continuar

[Vídeo: C++ Tutorial for Beginners, freeCodeCamp](https://www.youtube.com/watch?v=vLnPwxZdW4Y)

A miniatura vem do YouTube. O vídeo só carrega quando clicas. [Abrir no YouTube](https://www.youtube.com/watch?v=vLnPwxZdW4Y)

O curso acima revê estes fundamentos em vídeo. Para veres o que o compilador faz ao teu código, cola um exemplo no [Compiler Explorer](https://godbolt.org/) e observa o assembly gerado com `-O2` contra `-O0`.
