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¶
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:
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":
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:
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::stringsubstituichar[]— cresce sozinha, compara com==, concatena com+.autoinfere o tipo em tempo de compilação — não é tipagem dinâmica.constcria uma constante com tipo — mais seguro que#define.nullptrsubstituiNULL/0como ponteiro nulo.namespaceevita colisão de nomes; prefirastd::explícito ausing namespace std;.