Funções em C++

Onde o código se torna poesia... ou pelo menos tenta

Nossa Missão (caso você escolha aceitá-la)

  • Decifrar o enigma das funções
  • Sobreviver à passagem de parâmetros
  • Não se perder no labirinto do escopo

Aviso: Efeitos colaterais podem incluir debugação compulsiva e chamadas para o além da memória alocada.

Funções: As Divas do Código

Funções são como divas do rock:

  • Exigem atenção (e parênteses)
  • Têm seu próprio escopo (camarim)
  • Retornam algo (nem sempre o esperado)

Anatomia de uma Função


tipo_retorno nome_da_funcao(tipo_param param1, tipo_param param2) {
    // Corpo da função (onde a mágica acontece, ou não)
    return algo_supostamente_útil;
}
		    

Cada parte tem um propósito. Encontrá-lo é o seu trabalho.

Definição e Chamada de Funções


// Definição
tipo_retorno nome_funcao(parametros) {
    // Corpo da função
    return valor;
}

// Chamada
resultado = nome_funcao(argumentos);
                

Porque escrever uma vez não é suficiente, né?

Exemplo: Função de Saudação


#include <iostream>
#include <string>

void saudar(std::string nome) {
    std::cout << "Olá, " << nome << "!" << std::endl;
}

int main() {
    saudar("C++ Enthusiast");
    return 0;
}
                

Porque todo programador merece um "olá" personalizado!

Passagem de Parâmetros: O Dilema de Schrödinger

Por Valor

O gato está vivo. Ou morto. Mas é uma cópia.

Por Referência

O gato está vivo e morto. E é o original.

Passagem de Parâmetros

Por Valor


void dobrar(int x) {
    x *= 2;  // Altera apenas a cópia local
}

int main() {
    int num = 5;
    dobrar(num);  // num ainda é 5
    return 0;
}
                

Quando você quer manter seus originais intactos...

Passagem de Parâmetros

Por Referência


void dobrar(int& x) {
    x *= 2;  // Altera o valor original
}

int main() {
    int num = 5;
    dobrar(num);  // num agora é 10
    return 0;
}
                

Quando você está pronto para um compromisso sério com seus dados.

Exemplo: O Experimento de Schrödinger


#include <iostream>

void porValor(int gato) {
    gato = 0; // O gato da cópia está morto
}

void porReferencia(int &gato) {
    gato = 0; // O gato original está morto
}

int main() {
    int gatoVivo = 1;
    
    porValor(gatoVivo);
    std::cout << "Gato após valor: " << gatoVivo << std::endl; // Ainda vivo
    
    porReferencia(gatoVivo);
    std::cout << "Gato após referência: " << gatoVivo << std::endl; // Morto
    
    return 0;
}
                

Escopo: As Bolhas de Realidade do Código

Variáveis Locais

Vivem em uma bolha de realidade limitada

Variáveis Globais

Onipresentes. O Big Brother do seu código.

Escopo de Variáveis e Funções


#include <iostream>

int variavel_global = 42;

void funcao_exemplo() {
    int variavel_local = 10;
    std::cout << variavel_global << std::endl;  // OK
    std::cout << variavel_local << std::endl;   // OK
}

int main() {
    std::cout << variavel_global << std::endl;  // OK
    // std::cout << variavel_local << std::endl;  // Erro!
    return 0;
}
                

Variáveis globais: o canivete suíço que você não deveria usar.

Exemplo: A Vida Secreta das Variáveis


#include <iostream>

int varGlobal = 42; // Observando tudo, como um estudante ansioso

void funcaoA() {
    int varLocal = 10; // Existe apenas neste universo paralelo
    std::cout << "Global em A: " << varGlobal << std::endl;
    std::cout << "Local em A: " << varLocal << std::endl;
}

void funcaoB() {
    varGlobal++; // Manipulando a realidade
    // varLocal? Que varLocal? Isso é uma ilusão.
    std::cout << "Global em B: " << varGlobal << std::endl;
}

int main() {
    funcaoA();
    funcaoB();
    std::cout << "Global em main: " << varGlobal << std::endl;
    // Tentando acessar varLocal aqui é como procurar sentido em um código legado
    return 0;
}
                

Funções Recursivas: O Pesadelo de Inception

Funções dentro de funções dentro de funções... Até o compilador ficar tonto.

Exemplo: Fatorial Recursivo (ou Como Quebrar a Pilha com Estilo)


#include <iostream>

unsigned long long fatorial(unsigned int n) {
    if (n <= 1) return 1; // Caso base, ou "finalmente, posso sair daqui"
    return n * fatorial(n - 1); // Mais fundo na toca do coelho
}

int main() {
    std::cout << "5! = " << fatorial(5) << std::endl; // 120
    std::cout << "20! = " << fatorial(20) << std::endl; // Grande
    // std::cout << "1000000! = " << fatorial(1000000) << std::endl; // Não tente isso em casa
    return 0;
}
                

Cuidado: efeitos colaterais podem incluir estouro de pilha e dor de cabeça.

Boas Práticas para Nomear Funções

  • Use verbos para ações: calcularTotal()
  • Seja descritivo: converterCelsiusParaFahrenheit()
  • Use CamelCase: verificarSaldoConta()
  • Evite abreviações: obterUsuarioPorId() ao invés de getUsrById()
  • Mantenha consistência: iniciarProcesso(), pararProcesso()

Porque nomear funções é como contar piadas: se você precisa explicar, provavelmente não é bom.

Conclusão: Você Sobreviveu às Funções!

  • Funções em C++: Organizando o caos, uma linha de cada vez.
  • O escopo é o que separa os debugadores dos desesperados
  • Lembre-se: Um bom programador escreve código que humanos podem entender.

E se tudo der errado, pelo menos você sabe como chamar a função panic()!