Projeto Calculadora

O primeiro projeto do guia: uma calculadora que roda no terminal. Ela pede um número, a operação e outro número, mostra o resultado e pergunta se você quer fazer outra conta. Parece simples, e é de propósito: a conta é a parte fácil. O desafio é fazer o programa não quebrar quando alguém divide por zero, escolhe uma operação que não existe ou digita "abc" no lugar de um número.

Você vai praticar
  • Ler números e caracteres do teclado com cin.
  • Escolher a conta com switch (e o que acontece quando falta um break).
  • Tratar a divisão por zero de um double.
  • Repetir tudo com do-while.
  • Validar a entrada: o que fazer quando o cin falha.
  • Organizar o programa em funções.

O que vamos construir

Esta é a calculadora pronta, rodando na máquina da Ana. Tudo o que aparece depois dos dois-pontos nas perguntas foi digitado por ela:

$ ./calculadora
=== Calculadora da Ana ===
Primeiro número: 10
Operação (+ - * /): +
Segundo número: 5
10 + 5 = 15
Outra conta? (s/n): s
Primeiro número: seis
"seis" não é um número. Tente de novo.      « texto no lugar de número
Primeiro número: 6
Operação (+ - * /): %
Operação desconhecida: %                    « operação que não existe
Operação (+ - * /): *
Segundo número: 1.5
6 * 1.5 = 9
Outra conta? (s/n): s
Primeiro número: 1
Operação (+ - * /): /
Segundo número: 0
Não dá para dividir por zero.               « divisão por zero
Outra conta? (s/n): n
Até logo!

Vamos chegar lá em seis etapas. No fim de cada uma, o programa compila e roda:

  1. Ler dois números e somar (std::cin, double).
  2. Escolher a operação (char e switch).
  3. Não dividir por zero (if).
  4. Repetir até a pessoa sair (do-while).
  5. Validar o que foi digitado (cin falhando, clear, getline).
  6. Organizar em funções.

Antes de começar

Você precisa saber criar, compilar e rodar um programa (Olá, Mundo e Compilar com g++), usar variáveis double e char e ter visto o básico do cin. No seu computador, crie a pasta do projeto; o arquivo vai se chamar calculadora.cpp. Como fazer cada etapa no editor ou no terminal está em Como Usar os Projetos.

$ mkdir -p cpp/calculadora
$ cd cpp/calculadora

Etapa 1 · Ler dois números e somar

O começo de toda calculadora: pedir dois números e mostrar a soma. Os números são double (com casas decimais), porque a Ana quer fazer contas como 2.5 + 1.25.

#include <iostream>

int main() {
    std::cout << "=== Calculadora da Ana ===\n";
    double a = 0.0;
    double b = 0.0;

    std::cout << "Primeiro número: ";
    std::cin >> a;
    std::cout << "Segundo número: ";
    std::cin >> b;

    std::cout << a << " + " << b << " = " << a + b << '\n';
}
LinhaO que faz
#include <iostream>Traz o std::cout (saída) e o std::cin (entrada).
double a = 0.0;Cria a variável do primeiro número, já com zero. Toda variável começa com um valor (veja Boas Práticas).
std::cout << "Primeiro número: ";Mostra a pergunta sem \n: o cursor fica na mesma linha, e o que a pessoa digitar aparece logo depois dos dois-pontos.
std::cin >> a;Espera a pessoa digitar e apertar Enter, converte o texto num double e guarda em a. O cin (de character input, entrada de caracteres) é o teclado.
a << " + " << b << " = " << a + bMonta a frase pedaço por pedaço: número, texto, número, texto e a soma.
$ g++ -std=c++23 -Wall -Wextra calculadora.cpp -o calculadora
$ ./calculadora
=== Calculadora da Ana ===
Primeiro número: 10
Segundo número: 5
10 + 5 = 15
$ ./calculadora
=== Calculadora da Ana ===
Primeiro número: 2.5
Segundo número: 1.25
2.5 + 1.25 = 3.75

Repare: a Ana digitou 10, a variável é double, e o cout mostrou 10, sem .0. O cout só mostra as casas decimais que existem.

No C++, o decimal é com ponto, mesmo em português

O cin lê números no formato do C++, com ponto: 2.5. Se a Ana digitar 2,5, veja o que acontece:

$ ./calculadora
=== Calculadora da Ana ===
Primeiro número: 2,5
Segundo número: 2 + 0 = 2

O cin >> a leu o 2 e parou na vírgula, que não faz parte de um número. A vírgula e o 5 ficaram esperando na entrada. O cin >> b tentou ler um número começando pela vírgula, falhou, e colocou 0 em b, sem nem esperar a Ana digitar. Na etapa 5 você vai aprender a perceber essa falha.

Missão (etapa 1): o programa já lê o primeiro número. Peça e leia o segundo número na variável b e mostre a soma no formato 10 + 5 = 15. A entrada 10 e 5 já vem preenchida (no editor, o que a entrada "digita" não aparece na tela, só as perguntas e a resposta).

#include <iostream>

int main() {
    std::cout << "=== Calculadora da Ana ===\n";
    double a = 0.0;
    double b = 0.0;

    std::cout << "Primeiro número: ";
    std::cin >> a;
    // peça e leia o segundo número

    // mostre a soma no formato 10 + 5 = 15
}
#include <iostream>

int main() {
    std::cout << "=== Calculadora da Ana ===\n";
    double a = 0.0;
    double b = 0.0;

    std::cout << "Primeiro número: ";
    std::cin >> a;
    std::cout << "Segundo número: ";
    std::cin >> b;

    std::cout << a << " + " << b << " = " << a + b << '\n';
}

Etapa 2 · Escolher a operação

Agora a calculadora pergunta qual conta fazer. A operação é um único caractere (+, -, * ou /), então cabe num char (veja Caracteres). Para escolher a conta, um switch: ele compara o op com cada case e pula para o que combina.

#include <iostream>

int main() {
    std::cout << "=== Calculadora da Ana ===\n";
    double a = 0.0;
    double b = 0.0;
    char op = ' ';

    std::cout << "Primeiro número: ";
    std::cin >> a;
    std::cout << "Operação (+ - * /): ";
    std::cin >> op;
    std::cout << "Segundo número: ";
    std::cin >> b;

    double resultado = 0.0;
    switch (op) {
        case '+':
            resultado = a + b;
            break;
        case '-':
            resultado = a - b;
            break;
        case '*':
            resultado = a * b;
            break;
        case '/':
            resultado = a / b;
            break;
        default:
            std::cout << "Operação desconhecida: " << op << '\n';
            return 1;
    }
    std::cout << a << ' ' << op << ' ' << b << " = " << resultado << '\n';
}
char op = ' ';
Um caractere, começando com um espaço. Caracteres vão entre aspas simples: '+'. Aspas duplas ("+") seriam um texto, e o switch não aceita texto.
switch (op)
"Conforme o valor de op…".
case '+':
"…se for '+', comece aqui."
break;
"Terminei, saia do switch." Sem ele, o programa continua no case seguinte (veja o cenário abaixo).
default:
"Se não for nenhum dos anteriores." Aqui mostramos o erro e saímos com return 1;: o código de saída 1 avisa ao sistema que algo deu errado (0 quer dizer "tudo certo").
a << ' ' << op << ' ' << b
Mostra a conta com a operação escolhida, com espaços (' ', um char) entre as partes.
$ g++ -std=c++23 -Wall -Wextra calculadora.cpp -o calculadora
$ ./calculadora
=== Calculadora da Ana ===
Primeiro número: 10
Operação (+ - * /): /
Segundo número: 4
10 / 4 = 2.5
$ ./calculadora
=== Calculadora da Ana ===
Primeiro número: 7
Operação (+ - * /): %
Segundo número: 2
Operação desconhecida: %
$ printf "7\n%%\n2\n" | ./calculadora > /dev/null; echo $?
1                                            « o código de saída do return 1

A última linha usa um truque do terminal: o printf "digita" as três respostas, a saída do programa é jogada fora (> /dev/null) e o echo $? mostra o código de saída. Não se preocupe com ele; é só para você ver o 1.

Dá para digitar tudo numa linha

O cin >> separa os valores por espaços, não por linhas. Se a Ana digitar 10 * 3 na primeira pergunta, os três valores ficam esperando na entrada, e as perguntas seguintes nem param:

$ ./calculadora
=== Calculadora da Ana ===
Primeiro número: 10 * 3
Operação (+ - * /): Segundo número: 10 * 3 = 30
Esqueceu o break: a conta "escorrega" para o próximo case

Este código tem um erro de propósito: o break depois da soma foi apagado.

        case '+':
            resultado = a + b;
        case '-':
            resultado = a - b;
            break;
$ g++ -std=c++23 -Wall -Wextra calculadora.cpp -o calculadora
calculadora.cpp: In function ‘int main()’:
calculadora.cpp:19:23: warning: this statement may fall through [-Wimplicit-fallthrough=]
   19 |             resultado = a + b;
      |             ~~~~~~~~~~^~~~~~~
calculadora.cpp:20:9: note: here
   20 |         case '-':
      |         ^~~~
$ ./calculadora
=== Calculadora da Ana ===
Primeiro número: 10
Operação (+ - * /): +
Segundo número: 5
10 + 5 = 5

O que significa: "esta instrução pode cair para o próximo" (fall through, "cair através"). Sem o break, depois de somar o programa continuou no case '-' e trocou o resultado por 10 - 5. A conta "10 + 5 = 5" está errada, mas o programa rodou sem reclamar. Só o aviso do -Wextra denunciou o problema.

Como consertar: um break; no fim de cada case. Veja mais em switch.

Missão (etapa 2): esta versão só sabe somar e subtrair. Acrescente os case da multiplicação ('*') e da divisão ('/'), cada um com o seu break. A entrada da missão é 10, /, 4.

#include <iostream>

int main() {
    std::cout << "=== Calculadora da Ana ===\n";
    double a = 0.0;
    double b = 0.0;
    char op = ' ';

    std::cout << "Primeiro número: ";
    std::cin >> a;
    std::cout << "Operação (+ - * /): ";
    std::cin >> op;
    std::cout << "Segundo número: ";
    std::cin >> b;

    double resultado = 0.0;
    switch (op) {
        case '+':
            resultado = a + b;
            break;
        case '-':
            resultado = a - b;
            break;
        // acrescente aqui a multiplicação e a divisão
        default:
            std::cout << "Operação desconhecida: " << op << '\n';
            return 1;
    }
    std::cout << a << ' ' << op << ' ' << b << " = " << resultado << '\n';
}
#include <iostream>

int main() {
    std::cout << "=== Calculadora da Ana ===\n";
    double a = 0.0;
    double b = 0.0;
    char op = ' ';

    std::cout << "Primeiro número: ";
    std::cin >> a;
    std::cout << "Operação (+ - * /): ";
    std::cin >> op;
    std::cout << "Segundo número: ";
    std::cin >> b;

    double resultado = 0.0;
    switch (op) {
        case '+':
            resultado = a + b;
            break;
        case '-':
            resultado = a - b;
            break;
        case '*':
            resultado = a * b;
            break;
        case '/':
            resultado = a / b;
            break;
        default:
            std::cout << "Operação desconhecida: " << op << '\n';
            return 1;
    }
    std::cout << a << ' ' << op << ' ' << b << " = " << resultado << '\n';
}

Etapa 3 · Não dividir por zero

O que acontece se a Ana dividir por zero? Na matemática, não existe resposta. No C++, com double, existe uma resposta estranha:

$ ./calculadora
=== Calculadora da Ana ===
Primeiro número: 10
Operação (+ - * /): /
Segundo número: 0
10 / 0 = inf
$ ./calculadora
=== Calculadora da Ana ===
Primeiro número: -10
Operação (+ - * /): /
Segundo número: 0
-10 / 0 = -inf
$ ./calculadora
=== Calculadora da Ana ===
Primeiro número: 0
Operação (+ - * /): /
Segundo número: 0
0 / 0 = -nan

inf é "infinito" (infinity) e nan é "não é um número" (not a number). São valores especiais do double, explicados em float e double. O programa não quebrou, mas uma calculadora que responde "infinito" não ajuda ninguém. Com int seria pior: dividir um inteiro por zero é comportamento indefinido e costuma derrubar o programa.

A solução é conferir antes de dividir. Só o case '/' muda:

        case '/':
            if (b == 0) {
                std::cout << "Não dá para dividir por zero.\n";
                return 1;
            }
            resultado = a / b;
            break;
$ g++ -std=c++23 -Wall -Wextra calculadora.cpp -o calculadora
$ ./calculadora
=== Calculadora da Ana ===
Primeiro número: 10
Operação (+ - * /): /
Segundo número: 0
Não dá para dividir por zero.
$ ./calculadora
=== Calculadora da Ana ===
Primeiro número: 10
Operação (+ - * /): /
Segundo número: 4
10 / 4 = 2.5

O return 1; dentro do case encerra o main na hora: a linha que mostra o resultado nem chega a rodar.

Missão (etapa 3): rode como está: com a entrada 10, /, 0, a calculadora responde inf. Acrescente no case '/' o if que mostra Não dá para dividir por zero. e encerra com return 1;.

#include <iostream>

int main() {
    std::cout << "=== Calculadora da Ana ===\n";
    double a = 0.0;
    double b = 0.0;
    char op = ' ';

    std::cout << "Primeiro número: ";
    std::cin >> a;
    std::cout << "Operação (+ - * /): ";
    std::cin >> op;
    std::cout << "Segundo número: ";
    std::cin >> b;

    double resultado = 0.0;
    switch (op) {
        case '+':
            resultado = a + b;
            break;
        case '-':
            resultado = a - b;
            break;
        case '*':
            resultado = a * b;
            break;
        case '/':
            resultado = a / b;
            break;
        default:
            std::cout << "Operação desconhecida: " << op << '\n';
            return 1;
    }
    std::cout << a << ' ' << op << ' ' << b << " = " << resultado << '\n';
}
#include <iostream>

int main() {
    std::cout << "=== Calculadora da Ana ===\n";
    double a = 0.0;
    double b = 0.0;
    char op = ' ';

    std::cout << "Primeiro número: ";
    std::cin >> a;
    std::cout << "Operação (+ - * /): ";
    std::cin >> op;
    std::cout << "Segundo número: ";
    std::cin >> b;

    double resultado = 0.0;
    switch (op) {
        case '+':
            resultado = a + b;
            break;
        case '-':
            resultado = a - b;
            break;
        case '*':
            resultado = a * b;
            break;
        case '/':
            if (b == 0) {
                std::cout << "Não dá para dividir por zero.\n";
                return 1;
            }
            resultado = a / b;
            break;
        default:
            std::cout << "Operação desconhecida: " << op << '\n';
            return 1;
    }
    std::cout << a << ' ' << op << ' ' << b << " = " << resultado << '\n';
}

Etapa 4 · Repetir até a pessoa sair

Até agora, a calculadora faz uma conta e termina. Para fazer outra, a Ana precisa rodar o programa de novo. Vamos colocar tudo dentro de um laço que, no fim de cada conta, pergunta "Outra conta? (s/n)".

Qual laço? A conta precisa acontecer pelo menos uma vez, e a pergunta "continuar?" vem no fim. É exatamente o caso do do-while: "faça isto, e repita enquanto a resposta for s".

Há um detalhe: os return 1; das etapas anteriores encerravam o programa inteiro. Agora, um erro (operação desconhecida, divisão por zero) deve só pular a conta, não fechar a calculadora. Por isso entra uma variável deu_certo, que começa true e vira false quando algo dá errado.

#include <iostream>

int main() {
    std::cout << "=== Calculadora da Ana ===\n";
    char resposta = 'n';

    do {
        double a = 0.0;
        double b = 0.0;
        char op = ' ';

        std::cout << "Primeiro número: ";
        std::cin >> a;
        std::cout << "Operação (+ - * /): ";
        std::cin >> op;
        std::cout << "Segundo número: ";
        std::cin >> b;

        double resultado = 0.0;
        bool deu_certo = true;
        switch (op) {
            case '+':
                resultado = a + b;
                break;
            case '-':
                resultado = a - b;
                break;
            case '*':
                resultado = a * b;
                break;
            case '/':
                if (b == 0) {
                    std::cout << "Não dá para dividir por zero.\n";
                    deu_certo = false;
                } else {
                    resultado = a / b;
                }
                break;
            default:
                std::cout << "Operação desconhecida: " << op << '\n';
                deu_certo = false;
        }
        if (deu_certo) {
            std::cout << a << ' ' << op << ' ' << b << " = " << resultado << '\n';
        }

        std::cout << "Outra conta? (s/n): ";
        std::cin >> resposta;
    } while (resposta == 's' || resposta == 'S');

    std::cout << "Até logo!\n";
}
Linha novaO que faz
char resposta = 'n';Guarda o s ou n. Fica fora do laço, porque o while lá no fim precisa enxergá-la (veja Escopo).
do {Começa o bloco que se repete. As variáveis a, b e op agora nascem dentro dele: a cada conta, começam do zero.
bool deu_certo = true;"Até agora, tudo certo." Os casos de erro mudam para false em vez de return 1.
if (deu_certo) { … }Só mostra o resultado quando a conta foi feita.
} while (resposta == 's' || resposta == 'S');Volta para o do se a resposta for s ou S. Qualquer outra coisa encerra o laço. O ; no fim é obrigatório no do-while.
$ g++ -std=c++23 -Wall -Wextra calculadora.cpp -o calculadora
$ ./calculadora
=== Calculadora da Ana ===
Primeiro número: 10
Operação (+ - * /): +
Segundo número: 5
10 + 5 = 15
Outra conta? (s/n): s
Primeiro número: 7
Operação (+ - * /): /
Segundo número: 0
Não dá para dividir por zero.               « o erro não fechou a calculadora
Outra conta? (s/n): s
Primeiro número: 3
Operação (+ - * /): x
Segundo número: 3
Operação desconhecida: x
Outra conta? (s/n): n
Até logo!

Missão (etapa 4): o código já tem o deu_certo e a pergunta "Outra conta?", mas ainda faz uma conta só. Coloque tudo, da criação de a até a leitura da resposta, dentro de um do { … } while (resposta == 's' || resposta == 'S');. A entrada da missão faz duas contas (10 + 5 e 3 * 4) e responde n.

#include <iostream>

int main() {
    std::cout << "=== Calculadora da Ana ===\n";
    char resposta = 'n';
    // repita tudo daqui até o "Outra conta?" enquanto a resposta for s

    double a = 0.0;
    double b = 0.0;
    char op = ' ';

    std::cout << "Primeiro número: ";
    std::cin >> a;
    std::cout << "Operação (+ - * /): ";
    std::cin >> op;
    std::cout << "Segundo número: ";
    std::cin >> b;

    double resultado = 0.0;
    bool deu_certo = true;
    switch (op) {
        case '+':
            resultado = a + b;
            break;
        case '-':
            resultado = a - b;
            break;
        case '*':
            resultado = a * b;
            break;
        case '/':
            if (b == 0) {
                std::cout << "Não dá para dividir por zero.\n";
                deu_certo = false;
            } else {
                resultado = a / b;
            }
            break;
        default:
            std::cout << "Operação desconhecida: " << op << '\n';
            deu_certo = false;
    }
    if (deu_certo) {
        std::cout << a << ' ' << op << ' ' << b << " = " << resultado << '\n';
    }

    std::cout << "Outra conta? (s/n): ";
    std::cin >> resposta;

    std::cout << "Até logo!\n";
}
#include <iostream>

int main() {
    std::cout << "=== Calculadora da Ana ===\n";
    char resposta = 'n';

    do {
        double a = 0.0;
        double b = 0.0;
        char op = ' ';

        std::cout << "Primeiro número: ";
        std::cin >> a;
        std::cout << "Operação (+ - * /): ";
        std::cin >> op;
        std::cout << "Segundo número: ";
        std::cin >> b;

        double resultado = 0.0;
        bool deu_certo = true;
        switch (op) {
            case '+':
                resultado = a + b;
                break;
            case '-':
                resultado = a - b;
                break;
            case '*':
                resultado = a * b;
                break;
            case '/':
                if (b == 0) {
                    std::cout << "Não dá para dividir por zero.\n";
                    deu_certo = false;
                } else {
                    resultado = a / b;
                }
                break;
            default:
                std::cout << "Operação desconhecida: " << op << '\n';
                deu_certo = false;
        }
        if (deu_certo) {
            std::cout << a << ' ' << op << ' ' << b << " = " << resultado << '\n';
        }

        std::cout << "Outra conta? (s/n): ";
        std::cin >> resposta;
    } while (resposta == 's' || resposta == 'S');

    std::cout << "Até logo!\n";
}

Etapa 5 · Validar o que foi digitado

A calculadora já se defende de operações erradas e da divisão por zero. Falta o pior caso: a pessoa digitar um texto onde se espera um número. Veja o que a versão da etapa 4 faz com abc:

$ ./calculadora
=== Calculadora da Ana ===
Primeiro número: abc
Operação (+ - * /): Segundo número: Operação desconhecida:
Outra conta? (s/n): Até logo!

A Ana digitou abc uma vez, e o programa disparou todas as perguntas sem esperar e terminou. O que aconteceu:

  1. O std::cin >> a tentou ler um número, encontrou abc e falhou. Ele coloca 0 em a e deixa o abc na entrada.
  2. Mais importante: o cin entra em estado de falha. Enquanto estiver assim, toda leitura seguinte falha na hora, sem esperar o teclado. Por isso o op ficou com o espaço do começo (Operação desconhecida: ) e a resposta ficou 'n'.

Para se defender, o programa precisa de três coisas (a aula Validar a Entrada explica cada uma com calma):

while (!(std::cin >> a))
O próprio std::cin >> a responde se deu certo: vale true se leu um número e false se falhou. O ! inverte: "enquanto a leitura falhar, repita".
std::cin.clear();
Tira o cin do estado de falha ("limpa" o erro), para ele voltar a ler.
std::getline(std::cin, lixo);
Lê o resto da linha (o abc) e guarda em lixo. Sem isso, o abc continuaria lá, e a próxima tentativa falharia de novo. De quebra, podemos mostrar o que a pessoa digitou.
if (std::cin.eof()) return 0;
eof = end of file, "fim do arquivo": a entrada acabou (a pessoa apertou Ctrl+D, ou a entrada veio de um arquivo que terminou). Aí não adianta pedir de novo: encerramos.

O código da etapa 5, com a validação nos dois números:

#include <iostream>
#include <string>

int main() {
    std::cout << "=== Calculadora da Ana ===\n";
    char resposta = 'n';

    do {
        double a = 0.0;
        double b = 0.0;
        char op = ' ';

        std::cout << "Primeiro número: ";
        while (!(std::cin >> a)) {
            if (std::cin.eof()) {
                return 0;
            }
            std::cin.clear();
            std::string lixo;
            std::getline(std::cin, lixo);
            std::cout << '"' << lixo << "\" não é um número. Tente de novo.\n";
            std::cout << "Primeiro número: ";
        }
        std::cout << "Operação (+ - * /): ";
        std::cin >> op;
        std::cout << "Segundo número: ";
        while (!(std::cin >> b)) {
            if (std::cin.eof()) {
                return 0;
            }
            std::cin.clear();
            std::string lixo;
            std::getline(std::cin, lixo);
            std::cout << '"' << lixo << "\" não é um número. Tente de novo.\n";
            std::cout << "Segundo número: ";
        }

        double resultado = 0.0;
        bool deu_certo = true;
        switch (op) {
            case '+':
                resultado = a + b;
                break;
            case '-':
                resultado = a - b;
                break;
            case '*':
                resultado = a * b;
                break;
            case '/':
                if (b == 0) {
                    std::cout << "Não dá para dividir por zero.\n";
                    deu_certo = false;
                } else {
                    resultado = a / b;
                }
                break;
            default:
                std::cout << "Operação desconhecida: " << op << '\n';
                deu_certo = false;
        }
        if (deu_certo) {
            std::cout << a << ' ' << op << ' ' << b << " = " << resultado << '\n';
        }

        std::cout << "Outra conta? (s/n): ";
        std::cin >> resposta;
    } while (resposta == 's' || resposta == 'S');

    std::cout << "Até logo!\n";
}
$ g++ -std=c++23 -Wall -Wextra calculadora.cpp -o calculadora
$ ./calculadora
=== Calculadora da Ana ===
Primeiro número: abc
"abc" não é um número. Tente de novo.
Primeiro número: 10
Operação (+ - * /): +
Segundo número: dez
"dez" não é um número. Tente de novo.
Segundo número: 10
10 + 10 = 20
Outra conta? (s/n): n
Até logo!

Funciona, mas repare: o bloco de validação aparece duas vezes, quase igual, uma para o a e outra para o b. Se a Ana quiser mudar a mensagem, vai ter de mudar nos dois lugares (e um dia vai esquecer um). Código repetido é o sinal clássico de que falta uma função, e é isso que a etapa 6 resolve.

Esqueceu o clear(): laço infinito

Sem o std::cin.clear();, o cin nunca sai do estado de falha. O getline também falha (não lê nada, por isso o "" vazio), a leitura do número falha de novo, e o laço gira para sempre. Para não travar o terminal, a Ana rodou com timeout 3 (para depois de 3 segundos) e head -6 (mostra só as 6 primeiras linhas):

$ printf "abc\n10\n" | timeout 3 ./calculadora | head -6
=== Calculadora da Ana ===
Primeiro número: "" não é um número. Tente de novo.
Primeiro número: "" não é um número. Tente de novo.
Primeiro número: "" não é um número. Tente de novo.
Primeiro número: "" não é um número. Tente de novo.
Primeiro número: "" não é um número. Tente de novo.

Como consertar: a ordem é sempre clear() primeiro, depois descartar a linha. Se o seu programa travar assim no terminal, aperte Ctrl+C.

Missão (etapa 5): esta é a versão da etapa 4. A entrada da missão começa com abc. Rode e veja o programa disparar as perguntas. Depois troque o std::cin >> a; por um laço de validação que mostre "abc" não é um número. Tente de novo. e pergunte de novo. Lembre do clear() e do getline (e do #include <string>).

#include <iostream>

int main() {
    std::cout << "=== Calculadora da Ana ===\n";
    char resposta = 'n';

    do {
        double a = 0.0;
        double b = 0.0;
        char op = ' ';

        std::cout << "Primeiro número: ";
        std::cin >> a;
        std::cout << "Operação (+ - * /): ";
        std::cin >> op;
        std::cout << "Segundo número: ";
        std::cin >> b;

        double resultado = 0.0;
        bool deu_certo = true;
        switch (op) {
            case '+':
                resultado = a + b;
                break;
            case '-':
                resultado = a - b;
                break;
            case '*':
                resultado = a * b;
                break;
            case '/':
                if (b == 0) {
                    std::cout << "Não dá para dividir por zero.\n";
                    deu_certo = false;
                } else {
                    resultado = a / b;
                }
                break;
            default:
                std::cout << "Operação desconhecida: " << op << '\n';
                deu_certo = false;
        }
        if (deu_certo) {
            std::cout << a << ' ' << op << ' ' << b << " = " << resultado << '\n';
        }

        std::cout << "Outra conta? (s/n): ";
        std::cin >> resposta;
    } while (resposta == 's' || resposta == 'S');

    std::cout << "Até logo!\n";
}
#include <iostream>
#include <string>

int main() {
    std::cout << "=== Calculadora da Ana ===\n";
    char resposta = 'n';

    do {
        double a = 0.0;
        double b = 0.0;
        char op = ' ';

        std::cout << "Primeiro número: ";
        while (!(std::cin >> a)) {
            if (std::cin.eof()) {
                return 0;
            }
            std::cin.clear();
            std::string lixo;
            std::getline(std::cin, lixo);
            std::cout << '"' << lixo << "\" não é um número. Tente de novo.\n";
            std::cout << "Primeiro número: ";
        }
        std::cout << "Operação (+ - * /): ";
        std::cin >> op;
        std::cout << "Segundo número: ";
        while (!(std::cin >> b)) {
            if (std::cin.eof()) {
                return 0;
            }
            std::cin.clear();
            std::string lixo;
            std::getline(std::cin, lixo);
            std::cout << '"' << lixo << "\" não é um número. Tente de novo.\n";
            std::cout << "Segundo número: ";
        }

        double resultado = 0.0;
        bool deu_certo = true;
        switch (op) {
            case '+':
                resultado = a + b;
                break;
            case '-':
                resultado = a - b;
                break;
            case '*':
                resultado = a * b;
                break;
            case '/':
                if (b == 0) {
                    std::cout << "Não dá para dividir por zero.\n";
                    deu_certo = false;
                } else {
                    resultado = a / b;
                }
                break;
            default:
                std::cout << "Operação desconhecida: " << op << '\n';
                deu_certo = false;
        }
        if (deu_certo) {
            std::cout << a << ' ' << op << ' ' << b << " = " << resultado << '\n';
        }

        std::cout << "Outra conta? (s/n): ";
        std::cin >> resposta;
    } while (resposta == 's' || resposta == 'S');

    std::cout << "Até logo!\n";
}

Etapa 6 · Organizar em funções

O main ficou comprido e tem código repetido. Vamos dividi-lo em funções, cada uma com uma tarefa e um nome que diz qual é:

FunçãoTarefaRecebe → devolve
ler_numeroMostrar uma pergunta e ler um número, repetindo até ser válido.a pergunta (const std::string&) → o número (double)
ler_operacaoLer a operação, repetindo até ser + - * /.nada → a operação (char)
calcularFazer a conta.dois números e a operação → o resultado (double)
quer_continuarPerguntar "Outra conta?".nada → true ou false
#include <cstdlib>
#include <iostream>
#include <string>

// Mostra a pergunta e lê um número. Repete até a pessoa digitar um número válido.
double ler_numero(const std::string& pergunta) {
    double numero = 0.0;
    std::cout << pergunta;
    while (!(std::cin >> numero)) {
        if (std::cin.eof()) {
            std::exit(0);  // a entrada acabou (Ctrl+D): encerra o programa
        }
        std::cin.clear();
        std::string lixo;
        std::getline(std::cin, lixo);
        std::cout << '"' << lixo << "\" não é um número. Tente de novo.\n";
        std::cout << pergunta;
    }
    return numero;
}

// Lê a operação. Repete até ser + - * ou /.
char ler_operacao() {
    char op = ' ';
    while (true) {
        std::cout << "Operação (+ - * /): ";
        if (!(std::cin >> op)) {
            std::exit(0);  // a entrada acabou
        }
        if (op == '+' || op == '-' || op == '*' || op == '/') {
            return op;
        }
        std::cout << "Operação desconhecida: " << op << '\n';
    }
}

// Faz a conta. Só recebe operações válidas (quem garante é ler_operacao).
double calcular(double a, char op, double b) {
    switch (op) {
        case '+': return a + b;
        case '-': return a - b;
        case '*': return a * b;
        case '/': return a / b;
    }
    return 0.0;
}

bool quer_continuar() {
    char resposta = 'n';
    std::cout << "Outra conta? (s/n): ";
    std::cin >> resposta;
    return resposta == 's' || resposta == 'S';
}

int main() {
    std::cout << "=== Calculadora da Ana ===\n";
    do {
        double a = ler_numero("Primeiro número: ");
        char op = ler_operacao();
        double b = ler_numero("Segundo número: ");

        if (op == '/' && b == 0) {
            std::cout << "Não dá para dividir por zero.\n";
        } else {
            std::cout << a << ' ' << op << ' ' << b << " = " << calcular(a, op, b) << '\n';
        }
    } while (quer_continuar());
    std::cout << "Até logo!\n";
}

Leia o main agora: ele cabe em poucas linhas e se lê quase como português. "Leia um número, leia a operação, leia outro número; se for divisão por zero, avise; senão, mostre a conta; repita enquanto a pessoa quiser continuar." Os detalhes de como cada coisa é feita estão escondidos nas funções.

const std::string& pergunta
A pergunta é passada por referência constante: a função usa o texto original, sem copiar e sem poder mudá-lo (aula Por Valor × Por Referência). Assim a mesma função serve para "Primeiro número: " e "Segundo número: ".
std::exit(0);
Encerra o programa inteiro, de dentro de qualquer função, com o código 0. Vem do <cstdlib>. Usamos só quando a entrada acabou e não há mais o que fazer.
while (true) { … return op; }
Em ler_operacao, o laço "infinito" termina pelo return, quando a operação é válida (veja break e continue).
case '+': return a + b;
Em calcular, cada case devolve o resultado direto: o return sai da função, então não precisa de break.
return 0.0;
Depois do switch. Na prática nunca chega aqui (o ler_operacao só deixa passar as quatro operações), mas toda função double precisa devolver algo em todos os caminhos. Sem essa linha, o g++ avisa: control reaches end of non-void function.
} while (quer_continuar());
A condição do laço é a própria chamada da função: ela pergunta, lê e devolve true ou false.
$ g++ -std=c++23 -Wall -Wextra calculadora.cpp -o calculadora
$ ./calculadora
=== Calculadora da Ana ===
Primeiro número: 10
Operação (+ - * /): +
Segundo número: 5
10 + 5 = 15
Outra conta? (s/n): s
Primeiro número: seis
"seis" não é um número. Tente de novo.
Primeiro número: 6
Operação (+ - * /): %
Operação desconhecida: %
Operação (+ - * /): *
Segundo número: 1.5
6 * 1.5 = 9
Outra conta? (s/n): s
Primeiro número: 1
Operação (+ - * /): /
Segundo número: 0
Não dá para dividir por zero.
Outra conta? (s/n): n
Até logo!

Uma diferença de comportamento: agora uma operação errada é pedida de novo na hora (% foi recusado e a pergunta voltou), em vez de pedir o segundo número e só depois reclamar.

Missão (etapa 6): o main já chama ler_numero("Primeiro número: "), mas a função não existe. Rode e veja o erro (‘ler_numero’ was not declared in this scope no g++; use of undeclared identifier 'ler_numero' no Clang). Depois escreva a função double ler_numero(const std::string& pergunta) com a validação da etapa 5. A entrada começa com seis.

#include <cstdlib>
#include <iostream>
#include <string>

// Escreva aqui a função ler_numero(pergunta):
// mostra a pergunta, lê um double e repete se não for número.

// Lê a operação. Repete até ser + - * ou /.
char ler_operacao() {
    char op = ' ';
    while (true) {
        std::cout << "Operação (+ - * /): ";
        if (!(std::cin >> op)) {
            std::exit(0);  // a entrada acabou
        }
        if (op == '+' || op == '-' || op == '*' || op == '/') {
            return op;
        }
        std::cout << "Operação desconhecida: " << op << '\n';
    }
}

// Faz a conta. Só recebe operações válidas (quem garante é ler_operacao).
double calcular(double a, char op, double b) {
    switch (op) {
        case '+': return a + b;
        case '-': return a - b;
        case '*': return a * b;
        case '/': return a / b;
    }
    return 0.0;
}

bool quer_continuar() {
    char resposta = 'n';
    std::cout << "Outra conta? (s/n): ";
    std::cin >> resposta;
    return resposta == 's' || resposta == 'S';
}

int main() {
    std::cout << "=== Calculadora da Ana ===\n";
    do {
        double a = ler_numero("Primeiro número: ");
        char op = ler_operacao();
        double b = ler_numero("Segundo número: ");

        if (op == '/' && b == 0) {
            std::cout << "Não dá para dividir por zero.\n";
        } else {
            std::cout << a << ' ' << op << ' ' << b << " = " << calcular(a, op, b) << '\n';
        }
    } while (quer_continuar());
    std::cout << "Até logo!\n";
}
#include <cstdlib>
#include <iostream>
#include <string>

// Mostra a pergunta e lê um número. Repete até a pessoa digitar um número válido.
double ler_numero(const std::string& pergunta) {
    double numero = 0.0;
    std::cout << pergunta;
    while (!(std::cin >> numero)) {
        if (std::cin.eof()) {
            std::exit(0);  // a entrada acabou (Ctrl+D): encerra o programa
        }
        std::cin.clear();
        std::string lixo;
        std::getline(std::cin, lixo);
        std::cout << '"' << lixo << "\" não é um número. Tente de novo.\n";
        std::cout << pergunta;
    }
    return numero;
}

// Lê a operação. Repete até ser + - * ou /.
char ler_operacao() {
    char op = ' ';
    while (true) {
        std::cout << "Operação (+ - * /): ";
        if (!(std::cin >> op)) {
            std::exit(0);  // a entrada acabou
        }
        if (op == '+' || op == '-' || op == '*' || op == '/') {
            return op;
        }
        std::cout << "Operação desconhecida: " << op << '\n';
    }
}

// Faz a conta. Só recebe operações válidas (quem garante é ler_operacao).
double calcular(double a, char op, double b) {
    switch (op) {
        case '+': return a + b;
        case '-': return a - b;
        case '*': return a * b;
        case '/': return a / b;
    }
    return 0.0;
}

bool quer_continuar() {
    char resposta = 'n';
    std::cout << "Outra conta? (s/n): ";
    std::cin >> resposta;
    return resposta == 's' || resposta == 'S';
}

int main() {
    std::cout << "=== Calculadora da Ana ===\n";
    do {
        double a = ler_numero("Primeiro número: ");
        char op = ler_operacao();
        double b = ler_numero("Segundo número: ");

        if (op == '/' && b == 0) {
            std::cout << "Não dá para dividir por zero.\n";
        } else {
            std::cout << a << ' ' << op << ' ' << b << " = " << calcular(a, op, b) << '\n';
        }
    } while (quer_continuar());
    std::cout << "Até logo!\n";
}

O código completo

Esta é a calculadora final, a mesma da etapa 6. Copie, compile com g++ -std=c++23 -Wall -Wextra calculadora.cpp -o calculadora e use à vontade:

#include <cstdlib>
#include <iostream>
#include <string>

// Mostra a pergunta e lê um número. Repete até a pessoa digitar um número válido.
double ler_numero(const std::string& pergunta) {
    double numero = 0.0;
    std::cout << pergunta;
    while (!(std::cin >> numero)) {
        if (std::cin.eof()) {
            std::exit(0);  // a entrada acabou (Ctrl+D): encerra o programa
        }
        std::cin.clear();
        std::string lixo;
        std::getline(std::cin, lixo);
        std::cout << '"' << lixo << "\" não é um número. Tente de novo.\n";
        std::cout << pergunta;
    }
    return numero;
}

// Lê a operação. Repete até ser + - * ou /.
char ler_operacao() {
    char op = ' ';
    while (true) {
        std::cout << "Operação (+ - * /): ";
        if (!(std::cin >> op)) {
            std::exit(0);  // a entrada acabou
        }
        if (op == '+' || op == '-' || op == '*' || op == '/') {
            return op;
        }
        std::cout << "Operação desconhecida: " << op << '\n';
    }
}

// Faz a conta. Só recebe operações válidas (quem garante é ler_operacao).
double calcular(double a, char op, double b) {
    switch (op) {
        case '+': return a + b;
        case '-': return a - b;
        case '*': return a * b;
        case '/': return a / b;
    }
    return 0.0;
}

bool quer_continuar() {
    char resposta = 'n';
    std::cout << "Outra conta? (s/n): ";
    std::cin >> resposta;
    return resposta == 's' || resposta == 'S';
}

int main() {
    std::cout << "=== Calculadora da Ana ===\n";
    do {
        double a = ler_numero("Primeiro número: ");
        char op = ler_operacao();
        double b = ler_numero("Segundo número: ");

        if (op == '/' && b == 0) {
            std::cout << "Não dá para dividir por zero.\n";
        } else {
            std::cout << a << ' ' << op << ' ' << b << " = " << calcular(a, op, b) << '\n';
        }
    } while (quer_continuar());
    std::cout << "Até logo!\n";
}

Repare no que o projeto juntou: cin, switch, if, do-while, while, validação, funções com parâmetros e retorno. Nenhum desses assuntos é difícil sozinho; o projeto mostra como eles se encaixam.


Desafios extras

  1. Resto da divisão. Acrescente a operação %. Cuidado: o % do C++ só funciona com inteiros. Para double, use std::fmod(a, b), do <cmath> (veja Matemática). E resto da divisão por zero também não existe.
  2. Potência. Acrescente ^ com std::pow(a, b).
  3. Histórico. Guarde cada conta feita num std::vector<std::string> e mostre todas antes do "Até logo!" (veja std::vector).
  4. Resultado bonito. Mostre o resultado com duas casas decimais, usando std::format ou std::println com {:.2f} (veja std::format e std::print).
  5. Lixo depois do número. Digite 12abc no primeiro número e descubra o que acontece (dica: o cin lê o 12 e deixa o abc). Como você faria a calculadora recusar essa entrada?
O erro que quase todo mundo encontra no desafio 1
$ g++ -std=c++23 -Wall -Wextra calculadora.cpp -o calculadora
calculadora.cpp: In function ‘double calcular(double, char, double)’:
calculadora.cpp:45:28: error: invalid operands of types ‘double’ and ‘double’ to binary ‘operator%’
   45 |         case '%': return a % b;
      |                          ~ ^ ~
      |                          |   |
      |                          |   double
      |                          double

O que significa: "operandos inválidos dos tipos double e double para o operador binário %". O desenho embaixo da linha mostra o tipo de cada lado. O % só aceita inteiros (veja Operadores Aritméticos).

Como consertar: return std::fmod(a, b); com #include <cmath>.

Desafio 1 no editor: acrescente a operação % com std::fmod. Mude três lugares: a condição de ler_operacao, o switch de calcular e o if da divisão por zero no main. Com a entrada da missão, a calculadora deve mostrar 10 % 3 = 1 e 7.5 % 2 = 1.5.

#include <cstdlib>
#include <iostream>
#include <string>

// Mostra a pergunta e lê um número. Repete até a pessoa digitar um número válido.
double ler_numero(const std::string& pergunta) {
    double numero = 0.0;
    std::cout << pergunta;
    while (!(std::cin >> numero)) {
        if (std::cin.eof()) {
            std::exit(0);  // a entrada acabou (Ctrl+D): encerra o programa
        }
        std::cin.clear();
        std::string lixo;
        std::getline(std::cin, lixo);
        std::cout << '"' << lixo << "\" não é um número. Tente de novo.\n";
        std::cout << pergunta;
    }
    return numero;
}

// Lê a operação. Repete até ser + - * ou /.
char ler_operacao() {
    char op = ' ';
    while (true) {
        std::cout << "Operação (+ - * /): ";
        if (!(std::cin >> op)) {
            std::exit(0);  // a entrada acabou
        }
        if (op == '+' || op == '-' || op == '*' || op == '/') {
            return op;
        }
        std::cout << "Operação desconhecida: " << op << '\n';
    }
}

// Faz a conta. Só recebe operações válidas (quem garante é ler_operacao).
double calcular(double a, char op, double b) {
    switch (op) {
        case '+': return a + b;
        case '-': return a - b;
        case '*': return a * b;
        case '/': return a / b;
    }
    return 0.0;
}

bool quer_continuar() {
    char resposta = 'n';
    std::cout << "Outra conta? (s/n): ";
    std::cin >> resposta;
    return resposta == 's' || resposta == 'S';
}

int main() {
    std::cout << "=== Calculadora da Ana ===\n";
    do {
        double a = ler_numero("Primeiro número: ");
        char op = ler_operacao();
        double b = ler_numero("Segundo número: ");

        if (op == '/' && b == 0) {
            std::cout << "Não dá para dividir por zero.\n";
        } else {
            std::cout << a << ' ' << op << ' ' << b << " = " << calcular(a, op, b) << '\n';
        }
    } while (quer_continuar());
    std::cout << "Até logo!\n";
}
#include <cmath>
#include <cstdlib>
#include <iostream>
#include <string>

// Mostra a pergunta e lê um número. Repete até a pessoa digitar um número válido.
double ler_numero(const std::string& pergunta) {
    double numero = 0.0;
    std::cout << pergunta;
    while (!(std::cin >> numero)) {
        if (std::cin.eof()) {
            std::exit(0);  // a entrada acabou (Ctrl+D): encerra o programa
        }
        std::cin.clear();
        std::string lixo;
        std::getline(std::cin, lixo);
        std::cout << '"' << lixo << "\" não é um número. Tente de novo.\n";
        std::cout << pergunta;
    }
    return numero;
}

// Lê a operação. Repete até ser + - * / ou %.
char ler_operacao() {
    char op = ' ';
    while (true) {
        std::cout << "Operação (+ - * / %): ";
        if (!(std::cin >> op)) {
            std::exit(0);  // a entrada acabou
        }
        if (op == '+' || op == '-' || op == '*' || op == '/' || op == '%') {
            return op;
        }
        std::cout << "Operação desconhecida: " << op << '\n';
    }
}

// Faz a conta. Só recebe operações válidas (quem garante é ler_operacao).
double calcular(double a, char op, double b) {
    switch (op) {
        case '+': return a + b;
        case '-': return a - b;
        case '*': return a * b;
        case '/': return a / b;
        case '%': return std::fmod(a, b);
    }
    return 0.0;
}

bool quer_continuar() {
    char resposta = 'n';
    std::cout << "Outra conta? (s/n): ";
    std::cin >> resposta;
    return resposta == 's' || resposta == 'S';
}

int main() {
    std::cout << "=== Calculadora da Ana ===\n";
    do {
        double a = ler_numero("Primeiro número: ");
        char op = ler_operacao();
        double b = ler_numero("Segundo número: ");

        if ((op == '/' || op == '%') && b == 0) {
            std::cout << "Não dá para dividir por zero.\n";
        } else {
            std::cout << a << ' ' << op << ' ' << b << " = " << calcular(a, op, b) << '\n';
        }
    } while (quer_continuar());
    std::cout << "Até logo!\n";
}

Na máquina da Ana, a solução roda assim:

$ ./calculadora
=== Calculadora da Ana ===
Primeiro número: 10
Operação (+ - * / %): %
Segundo número: 3
10 % 3 = 1
Outra conta? (s/n): s
Primeiro número: 7.5
Operação (+ - * / %): %
Segundo número: 2
7.5 % 2 = 1.5
Outra conta? (s/n): n
Até logo!

Exercícios

1. A Ana digitou 2,5 num std::cin >> a com a do tipo double. O que fica em a?

2. Num laço de validação, o que acontece se você esquecer o std::cin.clear()?

3. Por que a calculadora usa do-while e não while?

4. Quanto vale 10.0 / 0 com double, no g++?

5. Complete o laço que repete enquanto a leitura de a falhar:

while () {

6. Qual instrução tira o cin do estado de falha?

std::cin.;

Resumo

  • std::cin >> x lê números com ponto decimal; ele separa os valores por espaços, não por linhas.
  • O switch escolhe a conta; cada case precisa do seu break (o -Wextra avisa quando falta).
  • Divisão de double por zero dá inf ou nan; confira o divisor antes.
  • do { … } while (continuar); faz o "menu que repete"; um bool deu_certo pula a conta sem fechar o programa.
  • Quando o cin falha: teste a leitura, clear(), descarte a linha com getline e pergunte de novo. Trate o fim da entrada (eof()).
  • Código repetido pede uma função. Funções pequenas com nomes claros deixam o main legível.

Próximo projeto: Jogo de Adivinhação, com números aleatórios, contagem de tentativas e recorde.