Lista de exemplos de código C++

artigo de lista da Wikimedia

Esta é uma lista de exemplos de código C++, que demonstram a funcionalidade da linguagem e suas características.

Programa mínimo editar

Este é um exemplo de um programa mínimo que faz nada. Ele começa sua execução e logo termina. A função main é definida como o ponto de início de qualquer programa C++.

Notas com questões de compilador
Ainda que o padrão C++ não exija return 0; na função main, alguns compiladores antigos como o Microsoft Visual C++ 6 retornam avisos ou erros com tal situação e podem não gerar código objeto correto.
int main() {
}

O padrão C++ exige que main() retorne o tipo de dado int (inteiro).[1] Tradicionalmente, o valor do retorno dessa função representa o valor de retorno do próprio programa, o qual é informado para o processo que o executou. Um término mal sucedido pode ser indicado com um valor diferente de zero. O valor assumido por padrão é zero,[1] que representa retorno bem sucedido.

Programa Olá Mundo editar

Este é um exemplo do Programa Olá Mundo aplicado à linguagem C++ que utiliza a biblioteca padrão para a entrada e saída de dados.[2]

Notas com questões de compilador
A adição da biblioteca padrão <ostream> pode ser necessária para alguns compiladores. Isto deve-se ao fato do padrão ISO C++ exigir que a biblioteca padrão <iostream> declare o objeto std::cout como uma instância da classe std::ostream, mas não obriga que <iostream> defina a classe std::ostream e seus operadores[3].
#include <iostream>

int main() {
    std::cout << "Olá, Mundo!\n";
}

Nota-se no exemplo acima a declaração local de quais espaços de nomes estão sendo utilizados.

Algoritmo de Trabb Pardo-Knuth editar

 Ver artigo principal: Algoritmo de Trabb Pardo-Knuth

O código a seguir requer um compilador compatível com a revisão C++23:

#include <array>
#include <cmath>
#include <iostream>
#include <print>
#include <ranges>

double f(double t) {
    return sqrt(abs(t)) + 5 * pow(t, 3);
}

int main() {
    std::array<double, 11> a;
    for (auto& t : a) {
        std::cin >> t;
    }

    for (const auto [i, t] : a | std::views::enumerate | std::views::reverse) {
        if (auto y = f(t); y > 400) {
            std::println("{} TOO LARGE", i);
        } else {
            std::println("{} {}", i, y);
        }
    }
}

Gabaritos editar

Este é um exemplo da utilização de gabaritos para a substituição da seguinte macro de programação comumente utilizada em C, apesar de bastante insegura[4], para o retorno do maior entre dois elementos dados:

// versão utilizando macros

#define max(x, y) (x > y ? (x) : (y))

// versão utilizando gabaritos, muito mais segura e com checagem de tipo

template <typename T>
T max(T x, T y) {
   return (x > y) ? x : y;
}

Polimorfismo estático editar

Este é um exemplo de polimorfismo resolvido em tempo de compilação de código, representando a sobrecarga de funções.

extern void EnviaTrabalhoParaImpressora(TrabalhoTexto *, ImpressoraLaser *);
extern void EnviaTrabalhoParaImpressora(TrabalhoTexto *, ImpressoraTinta *);
extern void EnviaTrabalhoParaImpressora(TrabalhoHTML *, ImpressoraLaser *);
extern void EnviaTrabalhoParaImpressora(TrabalhoHTML *, ImpressoraTinta *);

Polimorfismo dinâmico editar

Este é um exemplo de polimorfismo resolvido em tempo de execução de código, representando funções virtuais.

#include <iostream>

class Ave { // classe base
public:
   virtual void MostraNome() {
      std::cout << "uma ave";
   }
   virtual ~Ave() {}
};

class Cisne : public Ave { // Cisne é uma Ave
public:
   void MostraNome() {
      std::cout << "um cisne"; // sobrecarrega a função virtual
   }
};

int main() {
   Ave *ave = new Cisne;

   ave->MostraNome(); // produz na saída "um cisne", e não "uma ave"

   delete ave;
}

Utilizando a biblioteca padrão editar

Este é um exemplo de programa que utiliza elementos da Standard Template Library.

#include <iostream>  // std::cout
#include <vector>    // std::vector<>
#include <map>       // std::map<> and std::pair<>
#include <algorithm> // std::for_each()
#include <string>    // std::string

int main() {
   std::map<std::string, std::vector<std::string>> people;

   // Adiciona algumas pessoas no mapeamento e permite que elas carreguem itens
   people["Anya"].push_back("livro");
   people["Dimitri"].push_back("computador pessoal");
   people["Anya"].push_back("casaco");

   // Percorre por todas as pessoas no contêiner com um lambda
   std::for_each(people.begin(), people.end(), [](const auto& person) {
      const auto& [name, items] = person; // Desestrutura o std::pair<>

      std::cout << name << " está carregando " << items.size();
      if (items.size() == 1) {
         std::cout << " item\n";
      } else {
         std::cout << " itens\n";
      }
   });
}

Busca de nomes dependente de argumento editar

O exemplo clássico de busca de nomes dependente de argumento, também chamada Koenig lookup, é mostrado abaixo:

namespace NS {
   class A {};
   void f(A) {}
}
 
int main() {
   NS::A a;
   f(a); // invoca NS::f
}

Note que não foi necessário especificar o espaço de nomes da função f, ainda que ela foi encontrada devido à sua associação com a classe A utilizada como argumento.

Tratamento de exceções editar

Abaixo é mostrado um exemplo do tratamento de exceções. A divisão de um inteiro por zero é uma operação inválida; como possui uma variável como divisor, a função div_ten_by deve primeiramente tratar casos inválidos de entrada. Caso a entrada seja zero, uma exceção será lançada, e o código será redirecionado para o tratamento da exceção (indicado por catch).

#include <cstdlib>
#include <iostream>

[[nodiscard]] // Resultado não deve ser ignorado
std::div_t div_ten_by(int divisor) {
   // Valida a entrada
   if (divisor == 0) {
      throw std::runtime_error("divisão por zero");
   }

   return std::div(10, divisor);
}

int main() {
   int divisor = 0;

   std::cout << "10 dividido por: ";
   std::cin >> divisor; // Obtém do usuário um número inteiro

   try {
      const auto [quot, rem] = div_ten_by(divisor); // Desestrutura o resultado
      std::cout << "\nQuociente: " << quot;
      std::cout << "\nResto: " << rem << std::endl;
   } catch (const std::exception& err) {
      std::cerr << "Erro: " << err.what() << std::endl;
      std::exit(EXIT_FAILURE);
   }
}

Manipulação de argumentos da linha de comando editar

Uma necessidade comum aos desenvolvedores é a comparação de cadeias de caracteres passados pela linha de comando. Um exemplo de sua interpretação segue abaixo:

#include <cstdlib>
#include <iostream>
#include <span>

int main(int argc, char *argv[]) {
   // Cria um iterador seguro do ponteiro `argv` (requer C++20)
   for (const auto& arg : std::span(argv, argc)) {
      if (arg[0] != '-') {
         continue;
      }

      switch (arg[1]) {
         case 'r': // caso possua um r após o traço
            std::cout << "Argumento -r usado\n";
            break;
         case 'v': // caso possua um v após o traço
            std::cout << "Argumento -v usado\n";
            break;
         default:  // este é o valor de escape e sua respectiva mensagem:
            std::cerr << "Erro: argumento incompleto\n";
            std::exit(EXIT_FAILURE);
      }
   }
}

Diferente do restante de exemplos, notar que no exemplo acima a função main recebe argumentos em uma assinatura especial e que não pode ser alterada. É com esses parâmetros que o programa pode acessar as informações passadas pela linha de comando. O primeiro parâmetro passado indica a quantidade de parâmetros passados, e o segundo armazena uma cadeia de caracteres de cada parâmetro.

Ambiguidade da linguagem editar

Abaixo é mostrado um exemplo de código que demonstra um tipo de ambiguidade na linguagem C++, mostrando porque um analisador sintático trivial não é o suficiente para gerar uma árvore de sintaxe. Apesar da cadeia de caracteres foo<1>(3) aparecer duas vezes no código, ela está empregada em situações bastante diferentes, e envolvendo identificadores diferentes. Na primeira situação o identificador foo é uma variável local da função teste1, cujo valor é comparado com o literal 1. O resultado é finalmente comparado com o literal 3. Na segunda situação, o identificador foo é a invocação de uma instância da função gabarito definida no início do código, alimentando o gabarito com literal 1 e alimentando o parâmetro da função com o literal 3.

template <int N>
void foo(const int t) {
   // processa alguma coisa
}

void teste1() {
   int foo = 3;

   if (foo<1>(3)) { // situação 1
      // processa alguma coisa
   } else {
      // processa alguma coisa
   }
}

void teste2() {
   foo<1>(3); // situação 2
}

int main() {
   teste1();
   teste2();
}

Metaprogramação com gabarito editar

Um exemplo difundido no cálculo de fatorial é o uso de recursividade, como demonstrado abaixo:

int factorial(int n) {
   if (n == 0) {
      return 1;
   }

   return n * factorial(n - 1);
}

int x = factorial(4); // == (4 * 3 * 2 * 1) == 24
int y = factorial(0); // == 0! == 1

Note que no exemplo acima o cálculo é realizado em tempo de execução, o que implica que para cada chamada da função o processamento é feito novamente. É possível realizar tais cálculos recursivos em tempo de compilação através da metaprogramação com gabaritos, fazendo uso da especialização de gabaritos, com o seguinte código:

template <int N>
struct Factorial {
   enum { value = N * Factorial<N - 1>::value };
};

template <>
struct Factorial<0> {
   enum { value = 1 };
};

int x = Factorial<4>::value; // == 24
int y = Factorial<0>::value; // == 1

No exemplo acima, a metaprogramação com gabaritos permite a recursividade do gabarito, sendo que a condição de saída é dada pela especialização no valor literal "0". Para cada ocorrência de Factorial<4>::value no código fonte, o compilador realiza a chamada recursiva e substitui pelo resultado, 24. Em tempo de execução resta somente o valor literal, evitando o cálculo.

Referências

  1. a b Bjarne Stroustrup. «Bjarne Stroustrup's C++ Style and Technique FAQ :: Can I write "void main()"?» (em inglês). Consultado em 19 de junho de 2008 
  2. Juan Soulie (24 de abril de 2007). «C++ : Documentation : C++ Language Tutorial : Structure of a program» (em inglês). Consultado em 24 de outubro de 2007 
  3. Stroustrup discute sobre o assunto ao comentar sobre o erro em seu livro em relação ao padrão C++. Bjarne Stroustrup (4 de novembro de 2003). «Open issues for The C++ Programming Language (3rd Edition)» (em inglês). Página pessoal de Bjarne Stroustrup. Consultado em 2 de setembro de 2007. pg 633: I say "Manipulators taking istream and ostream are presented in and < ostream> respectively, and also in < iostream>. The rest of the standard manipulators are presented in < iomanip>." However, the standard disagrees and provides no manipulators in < iostream>… 
  4. Uma explicação genérica sobre a insegurança de macros encontra-se em:
    Marshall Cline (25 de setembro de 2006). «Inline functions: Why should I use inline functions instead of plain old #define macros?» (em inglês). Consultado em 10 de setembro de 2007 

Ver também editar