Estruturas de controle

A estrutura "if" / "else if" / "else"

Estruturas de controle (control structures, control flow) são construções da linguagem que nos permitem fazer coisas mais complexas do que apenas executar instruções de maneira sequencial. A primeira que veremos é a estrutura if/else if/else. Ela nos permite tomar decisões e executar instruções diferentes dada uma ou mais condições. No exemplo abaixo, temos um programa simples que informa o preço que um cliente deve pagar em um cinema onde:

  • Clientes abaixo de 10 anos de idade não pagam.
  • Clientes acima de 50 anos pagam meia-entrada.
  • Demais clientes pagam o valor normal da entrada.
#include <iostream>

int main()
{
   int age = 0;
   double const price = 20.0;
   std::cout << "Insira sua idade: ";
   std::cin >> age;

   std::cout << "Sua idade é " << age << '\n';

   if (age <= 10) {
       std::cout << "Você não precisa pagar!\n";
   } else if (age > 50) {
       std::cout << "Você paga apenas " << price/2 << "!\n";
   } else {
       std::cout << "Você paga " << price << ".\n";
   }
}

O exemplo acima introduz também uma forma de ler dados de entrada (input) providenciados pelo usuário. Note que a variável age é inicializada com o valor 0. Porém, a linha std::cin >> age; permite ao usuário que estiver executando o programa informar um novo valor para age. Desta forma, o valor de age que será utilizado pelo programa será conhecido apenas durante a execução.

No exemplo, a linha 10 sempre será executada depois que o usuário informar o valor de age. Porém, a linha 13 será executada apenas se (if) a variável age tiver um valor abaixo de 10. A construção else if permite fazer um novo teste caso o teste anterior falhe, ou seja, a condição não seja verdadeira. Por outro lado, utilizar apenas else faz com que, caso nenhuma das condições anteriores tenha sido satisfeita, o código dentro do bloco seguinte execute garantidamente. Experimente alterar o valor de age para fazer com que o programa siga cada uma das possibilidades.

As estruturas while e for

As estruturas de controle while e for servem para repetir um bloco de código até que uma condição seja satisfeita. Este tipo específico de estrutura de controle se chama laço (loop). No exemplo abaixo, o programa pergunta se o usuário deseja aprender C++. Enquanto (while) a resposta não for s (sim) nem n (não), a mesma pergunta aparecerá para o usuário. Uma vez que o usuário der uma resposta válida, um if imprime a reação correspondente à resposta do usuário.

#include <iostream>

int main()
{
   char answer = '\0';

   while (answer != 's' && answer != 'n') {
       std::cout << "Você deseja aprender C++? [s/n]: ";
       std::cin >> answer;
   }

   if (answer == 's') {
       std::cout << "Esse é o espírito!\n";
   } else {
       std::cout << "Que triste...\n";
   }
}

Em geral, usamos o laço while quando não existe uma definição clara de quantas vezes precisamos rodar o laço, apenas uma condição de continuidade que deve em algum momento posterior tornar-se falsa.

O laço for adiciona legibilidade ao código quando temos uma inicialização e um passo que deve ser executado em toda iteração do laço. Tudo que é feito com um laço for pode também ser feito com um laço while, e vice versa. A diferença entre eles é que, em alguns casos, a leitura do código fica mais natural com um ou com outro. Ao contrário do while, o for se presta mais quando sabemos exatamente quantas vezes precisamos repetir as operações. No código abaixo, o laço for itera com valores de i = a até valor de i = b, somando 1 ao valor de i a cada iteração. Uma forma alternativa e mais compacta de escrever i = i + 1 seria escrever ++i.

#include <iostream>

int main()
{
   int a = 0;
   int b = 0;

   std::cout << "Digite o valor de A: ";
   std::cin >> a;

   std::cout << "Digite o valor de B: ";
   std::cin >> b;

   int sum = 0;

   for (int i = a; i <= b; i = i + 1) {
       sum += i;
   }

   std::cout << "Sum [" << a << ", " << b << "] = " << sum << std::endl;

   return 0;
}

As instruções continue e break

Quando utilizamos um laço, é possível que queiramos interrompê-lo prematuramente. Para isso existe a instrução break:

#include <iostream>

int main()
{
    for (int i = 1; i <= 100; i = i + 1) {
        std::cout << "Iteração #" << i << '\n'
                  << "Você deseja continuar? [s/n]: ";

        char op;
        std::cin >> op;
        if (op == 'n')
            break;
        
        std::cout << "Incrementando i..." << std::endl;
    }

    return 0;
}

Assim que a execução do programa atinge a instrução break, o laço é imediatamente interrompido; o i deixa de ser incrementado e vamos direto para nosso return.

Caso queiramos interromper a execução do corpo (a instrução composta, delimitada por chaves) de nosso laço mas continuar iterando, devemos utilizar a instrução continue:

#include <iostream>

int main()
{
    for (int i = 1; i <= 100; i = i + 1) {
        std::cout << "Iteração #" << i << '\n'
                  << "Você deseja pular essa iteração? [s/n]: ";

        char op;
        std::cin >> op;
        if (op == 's')
            continue;
        
        std::cout << "Executando o resto do corpo do laço" << std::endl;
    }

    return 0;
}

Perceba que, como i = i + 1 não faz parte do corpo do laço, ele é executado independentemente do continue. Lembre-se que isso não se aplica ao break.

É possível que você não veja vantagens imediatas no uso dessas duas instruções, mas elas podem simplificar o código e às vezes são até necessárias.

Devo utilizar { e }?

Você pode ter percebido que o corpo das instruções if (e suas partes else), for e while podem ser tanto uma instrução simples quanto uma instrução composta. Quando estas estruturas de controle consistem apenas de uma instrução, não é obrigatório utilizar chaves. Por exemplo, os dois if seguintes são equivalentes:

if (a) {
    b = 5;
}

if (a)
    b = 5;

Agora, quando precisamos de mais de uma instrução, precisamos agrupá-las como uma só:

if (a) {
    b = 5;
    c = 6;
} // Ok! Ambas as atribuições fazem parte do if

if (a)
    b = 5;
    c = 6; // Ops! Essa instrução não faz parte do if 

Alguns programadores optam por utilizar sempre instruções compostas para evitar surpresas. Há bons argumentos tanto a favor quanto contra isso, portanto ao trabalhar em um projeto preexistente siga o padrão de estilo que já esteja sendo utilizado.