C++: Objetos de Função STL

Na lição 37 aprendemos sobre iteradores.

Agora, vamos aprender sobre a "alma" dos algoritmos STL — objetos de função.

Algoritmos são o esqueleto, objetos de função são a carne. Somente quando combinados eles liberam o verdadeiro poder da STL.


1. Visão Geral de Objetos de Função

(1) 1.1 O que são Objetos de Função?

Objetos de função (Funtores) são objetos que podem ser chamados como funções.

Três tipos de objetos de função:

  1. Ponteiros de função
  2. Classes de objeto de função (sobrecarga de operator())
  3. Expressões lambda (C++11)

(2) 1.2 Por que Precisamos de Objetos de Função?

Os algoritmos STL são genéricos, mas operações específicas variam conforme a necessidade. Objetos de função permitem que você personalize operações.

Analogia do mundo real:



2. Ponteiros de Função

(1) 2.1 Uso Básico

Exemplo: Ordenação personalizada com ponteiros de função (Dificuldade ⭐⭐)

▶ Exemplo 2: Uso de Contêiner STL (Dificuldade ⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>

// Custom comparison function
bool compareDesc(int a, int b) {
 return a > b; // Descending
}

int main() {
 std::vector<int> v = {3, 1, 4, 1, 5, 9, 2, 6};
 
 // Use function pointer
 std::sort(v.begin(), v.end(), compareDesc);
 
 for (int x : v) {
 std::cout << x << " ";
 }
 std::cout << std::endl;
 
 return 0;
}
▶ Experimente

Saída:

TEXT 📖 Somente leitura
3 1 4 1 5 9 2 6

Resultado:

TEXT 📖 Somente leitura
9 6 5 4 3 2 2 1 1 

💡 Dica:



3. Classes de Objeto de Função

(1) 3.1 O que são Classes de Objeto de Função?

Classes de objeto de função são classes que sobrecarregam operator(), cujas instâncias podem ser chamadas como funções.

Exemplo: Comparador personalizado (Dificuldade ⭐⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

// Function object class: sort by string length
struct CompareByLength {
 bool operator()(const std::string& a, const std::string& b) const {
 return a.length() < b.length();
 }
};

int main() {
 std::vectorstd::string words = {"apple", "banana", "cat", "dog"};
 
 // Use function object
 std::sort(words.begin(), words.end(), CompareByLength());
 
 for (const auto& w : words) {
 std::cout << w << " ";
 }
 std::cout << std::endl;
 
 return 0;
}

Resultado:

TEXT 📖 Somente leitura
cat dog apple banana 

(2) 3.2 Vantagens dos Objetos de Função

Comparação Ponteiro de Função Classe de Objeto de Função
Estado Sem estado Pode ter estado (variáveis membro)
Desempenho Pode não ser inline Pode ser inline, mais rápido
Flexibilidade Baixa Alta (pode ser template)

(3) 3.3 Objetos de Função com Estado

Exemplo: Objeto de função contador (Dificuldade ⭐⭐⭐)

CPP
#include <iostream>
#include <algorithm>
#include <vector>

// Function object: count elements satisfying a condition
struct Counter {
 int threshold; // Threshold (state)
 
 Counter(int t) : threshold(t) {}
 
 bool operator()(int x) const {
 return x > threshold; // Count elements greater than threshold
 }
};

int main() {
 std::vector<int> v = {1, 5, 10, 15, 20};
 
 // Create function object, set threshold to 10
 Counter counter(10);
 
 // Count elements greater than 10
 int count = std::count_if(v.begin(), v.end(), counter);
 
 std::cout << "Elements greater than 10: " << count << std::endl; // Output: 2
 
 return 0;
}


4. Expressões Lambda

(1) 4.1 O que são Expressões Lambda?

Expressões lambda são funções anônimas introduzidas no C++11 que podem ser definidas inline ondequer que uma função seja necessária.

Sintaxe básica:

CPP
[capture](parameters) -> return_type { body }
Parte Descrição
capture Lista de captura (capturar variáveis externas)
parameters Lista de parâmetros
return_type Tipo de retorno (pode ser omitido)
body Corpo da função

(2) 4.2 Exemplo Básico

Exemplo: Ordenação com Lambda (Dificuldade ⭐)

TEXT 📖 Somente leitura
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
 std::vector<int> v = {3, 1, 4, 1, 5, 9, 2, 6};
 
 // Sort with Lambda expression (descending)
 std::sort(v.begin(), v.end(), (int a, int b) {
 return a > b;
 });
 
 for (int x : v) {
 std::cout << x << " ";
 }
 std::cout << std::endl;
 
 return 0;
}

Saída:

TEXT 📖 Somente leitura
3 1 4 1 5 9 2 6

(3) 4.3 Lista de Captura

A lista de captura determina quais variáveis externas uma Lambda pode acessar.

Método de Captura Descrição
`` Não captura nada
[x] Captura x por valor
[&x] Captura x por referência
[=] Captura todas as variáveis por valor
[&] Captura todas as variáveis por referência
[this] Captura o ponteiro this (usado dentro de uma classe)

Exemplo: Lambda com estado (Dificuldade ⭐⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
 std::vector<int> v = {1, 5, 10, 15, 20};
 int threshold = 10;
 
 // Capture threshold by value
 int count = std::count_if(v.begin(), v.end(),
 [threshold](int x) {
 return x > threshold;
 });
 
 std::cout << "Elements greater than " << threshold << ": " << count << std::endl;
 
 return 0;
}


5. Objetos de Função Predefinidos da STL

(1) 5.1 Objetos de Função Aritméticos

O cabeçalho functional fornece objetos de função comuns:

Objeto de Função Finalidade Exemplo
std::plusT Adição std::plusint()
std::minusT Subtração std::minusint()
std::multipliesT Multiplicação std::multipliesint()
std::dividesT Divisão std::dividesint()
std::negateT Negação std::negateint()

Exemplo: Dobrando com multiplies (Dificuldade ⭐)

TEXT 📖 Somente leitura
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

int main() {
 std::vector<int> v = {1, 2, 3, 4, 5};
 
 // Double all elements
 std::transform(v.begin(), v.end(), v.begin(),
 std::bind(std::multipliesint(), std::placeholders::_1, 2));
 
 for (int x : v) {
 std::cout << x << " ";
 }
 std::cout << std::endl;
 
 return 0;
}

(2) 5.2 Objetos de Função de Comparação

Objeto de Função Finalidade
std::equal_toT Igual a
std::not_equal_toT Diferente de
std::greaterT Maior que
std::lessT Menor que
std::greater_equalT Maior ou igual a
std::less_equalT Menor ou igual a

(3) 5.3 Objetos de Função Lógicos

Objeto de Função Finalidade
std::logical_andT E lógico
std::logical_orT OU lógico
std::logical_notT NÃO lógico


6. Exemplo Abrangente

▶ Exemplo 1: Processador de Notas (Dificuldade ⭐⭐⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

struct Student {
 std::string name;
 int score;
};

int main() {
 std::vectorStudent students = {
 {"Zhang San", 85},
 {"Li Si", 92},
 {"Wang Wu", 78}
 };
 
 // 1. Sort by score descending
 std::sort(students.begin(), students.end(),
 (const Student& a, const Student& b) {
 return a.score > b.score;
 });
 
 // 2. Find the highest score
 auto max_it = std::max_element(students.begin(), students.end(),
 (const Student& a, const Student& b) {
 return a.score < b.score;
 });
 
 std::cout << "Highest score: " << max_it->name << " " << max_it->score << std::endl;
 
 // 3. Count passing students
 int passed = std::count_if(students.begin(), students.end(),
 (const Student& s) {
 return s.score >= 60;
 });
 
 std::cout << "Students passed: " << passed << std::endl;
 
 return 0;
}
▶ Experimente

Saída:

TEXT 📖 Somente leitura
Highest score:  
Students passed:

❓ Perguntas Frequentes

P: Qual é melhor — Lambda ou classes de objeto de função? R:- Operações simples → Lambda (código conciso) - Operações complexas / precisa de reuso → Classes de objeto de função (melhor manutenibilidade)


P: auto pode deduzir o tipo de uma Lambda? R: O tipo de uma Lambda é um tipo anônimo único; só pode ser deduzido com auto, não escrito como um tipo concreto.

TEXT 📖 Somente leitura
auto func = (int x) { return x * 2; };
// std::function<int(int)> func = ... // Also works, but with performance overhead

P: Quando devo usar std::function? R: Use std::function quando precisar armazenar objetos de função (como variáveis membro, valores de retorno, etc.).


▶ Exemplo 3: Expressão Lambda (Dificuldade ⭐)

CPP
#include <iostream>
#include <algorithm>
#include <vector>

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5};

    int sum = 0;
    std::for_each(v.begin(), v.end(), [&sum](int x) {
        sum += x;
    });

    std::cout << "Total: " << sum << std::endl;

    return 0;
}
▶ Experimente

Saída:

TEXT 📖 Somente leitura
1 2 3 4 5
💡 Dica: Sintaxe Lambda: [capture](parameters) { body }. [&sum] captura por referência, permitindo modificação de variáveis externas.


Tópico Pontos-chave
Ponteiros de função Simples mas funcionalidade limitada
Classes de objeto de função Operações personalizáveis, podem carregar estado
Lambda Funções anônimas, concisas e poderosas
Objetos de função predefinidos std::plus etc., em functional
Lista de captura Como Lambda acessa variáveis externas

📖 Resumo

📝 Exercícios

  1. Básico (Dificuldade ⭐): Crie um funtor (uma classe sobrecarregando operator()) que "compara dois inteiros", e teste-o com std::sort.

  2. Intermediário (Dificuldade ⭐⭐): Use std::function para armazenar diferentes tipos de objetos chamáveis (funções regulares, lambdas, funtores) e chamá-los uniformemente.

  3. Desafio (Dificuldade ⭐⭐⭐): Use std::bind para vincular argumentos parciais, gerando novos objetos chamáveis. Implemente um adaptador de função de "pré-preenchimento de argumentos".



Próxima lição: Adaptadores STL (#39)

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%