Pular para conteúdo

1. Primeiros passos em C++

Vídeo

01 - Entrada e Saída de Dados em C++ (vídeo 1 da playlist)

Você já sabe programar. O que muda agora é a ferramenta. Este capítulo cobre cinco coisas que aparecem em praticamente toda linha de código C++ que você vai escrever daqui pra frente: entrada/saída, strings, auto, const e namespace.

Entrada e saída: iostream no lugar de stdio.h

Em C você escrevia:

#include <stdio.h>

int main(void) {
    int idade;
    printf("Digite sua idade: ");
    scanf("%d", &idade);
    printf("Voce tem %d anos\n", idade);
    return 0;
}

O printf/scanf funciona, mas tem um problema estrutural: o formato (%d, %s, %f) e o tipo da variável são duas informações separadas, e nada impede que fiquem inconsistentes. Se você trocar int por long e esquecer de trocar %d por %ld, o programa compila e se comporta mal — o compilador não te avisa.

Em C++, a entrada e saída ficam em <iostream>, usando os operadores << (inserir na saída) e >> (extrair da entrada):

#include <iostream>

int main() {
    int idade;
    std::cout << "Digite sua idade: ";
    std::cin >> idade;
    std::cout << "Voce tem " << idade << " anos\n";
    return 0;
}

Repare: não existe formato para casar com o tipo. std::cin >> idade sabe que idade é int porque o compilador enxerga o tipo da variável — o mesmo mecanismo funciona para double, std::string, char, e para qualquer tipo que você criar (você vai ver isso de novo quando sobrecarregar operator<< para suas próprias classes, na Unidade 5).

std::cout e std::cin vivem dentro do namespace std — daí o std::. Falamos sobre namespace mais abaixo.

std::string no lugar de char[]

Em C, uma string é um vetor de char terminado em '\0', e você é responsável por reservar espaço suficiente:

char nome[50];
scanf("%49s", nome);  // torça para caber
strcat(nome, "!");     // torça para nao estourar o buffer

Isso é fonte constante de bug: buffer pequeno demais, strcpy sem checar tamanho, esquecer o \0. Em C++, std::string administra a própria memória — ela cresce conforme necessário:

#include <iostream>
#include <string>

int main() {
    std::string nome;
    std::cout << "Seu nome: ";
    std::cin >> nome;               // le ate o primeiro espaco
    std::cout << "Ola, " << nome << "!\n";
    return 0;
}

Repare no limite do >>: se a pessoa digitar Ana Paula, nome fica só com Ana — o >> para no primeiro espaço. Para ler a linha inteira, com espaços e tudo, use std::getline:

std::string nome_completo;
std::cout << "Nome completo: ";
std::getline(std::cin, nome_completo);  // le tudo ate o Enter

Escolha um dos dois e siga com ele. Misturar >> e getline no mesmo programa tem uma pegadinha que pega todo mundo na primeira vez — está explicada em Erros comuns, no fim do capítulo.

Operações comuns:

std::string a = "casa";
std::string b = "azul";
std::string c = a + " " + b;      // concatenacao com +
c += "!";                          // concatenacao no lugar
std::cout << c.size() << "\n";    // tamanho (nao precisa de strlen)
std::cout << c[0] << "\n";        // acesso por indice, igual char[]
bool vazio = c.empty();

std::string também é comparável diretamente com ==, <, etc. — nada de strcmp.

auto: deixe o compilador inferir o tipo

auto x = 10;         // x e int
auto y = 3.14;        // y e double
auto nome = std::string("Ana"); // nome e std::string

auto não torna C++ uma linguagem dinamicamente tipada — o tipo de x é decidido em tempo de compilação e não muda depois. auto só evita que você repita um tipo longo (isso fica mais evidente quando o tipo é algo como std::map<std::string, std::vector<int>>::iterator, que você vai ver na Unidade 2). Em código simples como o do exemplo acima, é comum preferir escrever o tipo explicitamente (int x = 10;) por clareza — usar auto é uma escolha de estilo, não uma obrigação.

const: prometendo que um valor não muda

const double PI = 3.14159;
const int MAX_TENTATIVAS = 3;

Diferente de #define PI 3.14159 em C (que é uma substituição de texto feita pelo pré-processador, sem tipo e sem checagem), const cria uma variável de verdade, com tipo, que o compilador impede de ser reatribuída:

const int x = 5;
x = 10; // erro de compilacao: nao pode atribuir a variavel const

Isso vale a pena aqui porque const volta com força total no próximo capítulo, aplicado a parâmetros de função (const std::string&) — é uma das ideias mais importantes do curso.

nullptr no lugar de NULL/0

Em C, um ponteiro nulo costuma ser NULL (que normalmente é só 0). Isso causa ambiguidade quando 0 também pode significar o número zero. C++ tem nullptr, que tem tipo próprio e só significa "ponteiro nulo":

int* p = nullptr;
if (p == nullptr) {
    std::cout << "ponteiro vazio\n";
}

namespace: evitando colisão de nomes

Se dois arquivos (ou duas bibliotecas) definirem uma função distancia, em C isso vira um erro de linkedição. C++ resolve isso com namespace, que agrupa nomes sob um rótulo:

namespace geometria {
    double distancia(double x1, double y1, double x2, double y2);
}

namespace fisica {
    double distancia(double posicao_inicial, double posicao_final);
}

Para usar, você qualifica com :: (geometria::distancia(...)) ou, dentro de um escopo pequeno, importa um nome específico com using:

using std::cout;
cout << "menos verboso\n"; // em vez de std::cout

Evite using namespace std; no topo do arquivo (é comum em tutoriais, mas importa todos os nomes de std de uma vez, o que facilita colisão de nomes exatamente no tipo de projeto onde os namespaces deveriam ajudar). Prefira std:: explícito, ou um using pontual para o que você realmente vai usar bastante.

Erros comuns

"error: ‘cout’ is not a member of ‘std’" (ou similar) — normalmente falta #include <iostream>.

"error: ‘cin’ was not declared in this scope" — você esqueceu o std:: e não tem using namespace std;. Adicione o prefixo.

Comparar string com == e não compilar — confira se você está comparando std::string com std::string (ou com "literal" entre aspas duplas), não com 'c' (aspas simples, que é char, não string).

std::getline depois de std::cin >> vem vazio — este é o mais comum de todos, e parece um bug da linguagem. Quando você digita Ana e aperta Enter, o >> consome o Ana e deixa o \n para trás, no buffer de entrada. O getline seguinte encontra esse \n imediatamente, conclui que a linha acabou ali e devolve uma string vazia — sem nem esperar você digitar:

std::string nome, sobrenome;
std::cin >> nome;                   // voce digita "Ana" e aperta Enter
std::getline(std::cin, sobrenome);  // sobrenome fica VAZIO: leu so o \n que sobrou

São duas as saídas. A primeira é descartar o resto da linha antes do getline:

std::cin >> nome;
std::cin.ignore(10000, '\n');       // joga fora o que sobrou, ate o fim da linha
std::getline(std::cin, sobrenome);  // agora le a proxima linha de verdade

(ignore(n, c) descarta até n caracteres, parando se encontrar c. O 10000 é só um número grande o bastante para a linha.)

A segunda, mais simples quando dá: não misture os dois. Se todas as leituras do programa forem getline, ou todas forem >>, o problema nunca aparece.

Resumo

  • <iostream> (std::cin/std::cout) substitui <stdio.h> — sem formato para casar com tipo.
  • std::string substitui char[] — cresce sozinha, compara com ==, concatena com +.
  • auto infere o tipo em tempo de compilação — não é tipagem dinâmica.
  • const cria uma constante com tipo — mais seguro que #define.
  • nullptr substitui NULL/0 como ponteiro nulo.
  • namespace evita colisão de nomes; prefira std:: explícito a using namespace std;.