C++笔记 仿函数(函数对象)

什么是仿函数?

仿函数是重载了函数调用操作符 () 的类或结构体。它让对象可以像函数一样被调用,同时还能保存状态。

基本语法和实现

cpp

复制代码
#include <iostream>
using namespace std;

// 最简单的仿函数
class Add {
public:
    // 重载函数调用操作符
    int operator()(int a, int b) {
        return a + b;
    }
};

// 带状态的仿函数
class Multiplier {
private:
    int factor;
public:
    Multiplier(int f) : factor(f) {}  // 构造函数设置倍数
    
    int operator()(int x) {
        return x * factor;
    }
};

int main() {
    Add add;
    cout << add(5, 3) << endl;  // 输出: 8
    
    Multiplier doubleIt(2);
    Multiplier tripleIt(3);
    
    cout << doubleIt(10) << endl;  // 输出: 20
    cout << tripleIt(10) << endl;  // 输出: 30
    
    return 0;
}

仿函数的核心优势

1. 状态保持能力

cpp

复制代码
class Counter {
private:
    int count;
    string name;
public:
    Counter(string n = "counter") : count(0), name(n) {}
    
    int operator()() {
        cout << name << ": " << count << endl;
        return count++;
    }
    
    void reset() { count = 0; }
    int getCount() const { return count; }
};

int main() {
    Counter c1("主要计数器");
    Counter c2("备用计数器");
    
    c1();  // 主要计数器: 0
    c1();  // 主要计数器: 1
    c2();  // 备用计数器: 0
    c1();  // 主要计数器: 2
    
    return 0;
}

2. 模板编程中的威力

cpp

复制代码
#include <vector>
#include <algorithm>

// 通用的转换器仿函数
template<typename T>
class Transformer {
private:
    T offset;
public:
    Transformer(T o) : offset(o) {}
    
    T operator()(T x) const {
        return x + offset;
    }
};

// 使用仿函数的通用算法
template<typename Iterator, typename Func>
void transformRange(Iterator begin, Iterator end, Func func) {
    for (auto it = begin; it != end; ++it) {
        *it = func(*it);
    }
}

int main() {
    vector<int> numbers = {1, 2, 3, 4, 5};
    vector<double> decimals = {1.1, 2.2, 3.3, 4.4};
    
    Transformer<int> add5(5);
    Transformer<double> add2_5(2.5);
    
    transformRange(numbers.begin(), numbers.end(), add5);
    transformRange(decimals.begin(), decimals.end(), add2_5);
    
    for (auto num : numbers) cout << num << " ";    // 6 7 8 9 10
    cout << endl;
    for (auto num : decimals) cout << num << " ";  // 3.6 4.7 5.8 6.9
    cout << endl;
    
    return 0;
}

STL中的仿函数体系

内置仿函数分类

cpp

复制代码
#include <functional>
#include <algorithm>
#include <vector>

void demonstrateSTLFunctors() {
    vector<int> nums = {1, 2, 3, 4, 5};
    
    // 算术仿函数
    plus<int> add;
    minus<int> subtract;
    multiplies<int> multiply;
    divides<int> divide;
    modulus<int> mod;
    negate<int> negate;
    
    cout << "算术运算:" << endl;
    cout << "10 + 5 = " << add(10, 5) << endl;        // 15
    cout << "10 * 5 = " << multiply(10, 5) << endl;   // 50
    cout << "-10 = " << negate(10) << endl;           // -10
    
    // 关系仿函数
    greater<int> gt;
    less<int> lt;
    equal_to<int> eq;
    
    cout << "\n关系运算:" << endl;
    cout << "10 > 5: " << gt(10, 5) << endl;          // 1 (true)
    cout << "10 == 5: " << eq(10, 5) << endl;         // 0 (false)
    
    // 逻辑仿函数
    logical_and<bool> land;
    logical_or<bool> lor;
    logical_not<bool> lnot;
    
    cout << "\n逻辑运算:" << endl;
    cout << "true && false: " << land(true, false) << endl;  // 0
    cout << "!true: " << lnot(true) << endl;                 // 0
}

在STL算法中的应用

cpp

复制代码
#include <numeric>
#include <algorithm>

void stlAlgorithmsWithFunctors() {
    vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    // 1. 排序 - 使用仿函数自定义排序规则
    sort(nums.begin(), nums.end(), greater<int>());
    // nums: 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
    
    // 2. 累积计算 - 使用仿函数指定操作
    int sum = accumulate(nums.begin(), nums.end(), 0, plus<int>());
    int product = accumulate(nums.begin(), nums.end(), 1, multiplies<int>());
    
    cout << "总和: " << sum << endl;        // 55
    cout << "乘积: " << product << endl;    // 3628800
    
    // 3. 变换操作
    vector<int> result(nums.size());
    transform(nums.begin(), nums.end(), result.begin(), negate<int>());
    // result: -10, -9, -8, -7, -6, -5, -4, -3, -2, -1
}

高级仿函数技术

1. 仿函数适配器

cpp

复制代码
#include <functional>
#include <algorithm>

void functorAdapters() {
    vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    // 使用 bind2nd 绑定第二个参数(C++17 前)
    // auto greater_than_5 = bind2nd(greater<int>(), 5);
    
    // 现代C++使用 bind
    using namespace std::placeholders;
    auto greater_than_5 = bind(greater<int>(), _1, 5);
    
    int count = count_if(nums.begin(), nums.end(), greater_than_5);
    cout << "大于5的元素个数: " << count << endl;  // 5
    
    // 使用 not1 取反
    auto not_greater_than_5 = bind(logical_not<bool>(), 
                                  bind(greater<int>(), _1, 5));
    
    count = count_if(nums.begin(), nums.end(), not_greater_than_5);
    cout << "不大于5的元素个数: " << count << endl;  // 5
}

2. 仿函数组合

cpp

复制代码
// 复杂的仿函数组合
class ComplexFilter {
private:
    int min_val;
    int max_val;
public:
    ComplexFilter(int min, int max) : min_val(min), max_val(max) {}
    
    bool operator()(int x) const {
        return x >= min_val && x <= max_val && x % 2 == 0;
    }
};

// 模板化的仿函数组合
template<typename F1, typename F2>
class AndComposition {
private:
    F1 f1;
    F2 f2;
public:
    AndComposition(F1 func1, F2 func2) : f1(func1), f2(func2) {}
    
    template<typename T>
    bool operator()(const T& x) const {
        return f1(x) && f2(x);
    }
};

int main() {
    vector<int> nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    
    // 使用组合仿函数
    ComplexFilter filter(3, 8);
    int count = count_if(nums.begin(), nums.end(), filter);
    cout << "3-8之间的偶数: " << count << endl;  // 3 (4,6,8)
    
    return 0;
}

实际应用案例

案例1:智能数据处理器

cpp

复制代码
class DataProcessor {
private:
    double scale_factor;
    double offset;
    int process_count;
public:
    DataProcessor(double scale, double off) 
        : scale_factor(scale), offset(off), process_count(0) {}
    
    double operator()(double input) {
        process_count++;
        double result = (input * scale_factor) + offset;
        cout << "处理 #" << process_count << ": " 
             << input << " -> " << result << endl;
        return result;
    }
    
    void setParameters(double scale, double off) {
        scale_factor = scale;
        offset = off;
    }
    
    int getProcessCount() const { return process_count; }
    void resetCount() { process_count = 0; }
};

int main() {
    DataProcessor processor(2.5, 10.0);
    vector<double> data = {1.0, 2.0, 3.0, 4.0};
    
    transform(data.begin(), data.end(), data.begin(), processor);
    // 输出:
    // 处理 #1: 1 -> 12.5
    // 处理 #2: 2 -> 15
    // 处理 #3: 3 -> 17.5  
    // 处理 #4: 4 -> 20
    
    cout << "总处理次数: " << processor.getProcessCount() << endl;  // 4
    
    return 0;
}

案例2:策略模式仿函数

cpp

复制代码
// 排序策略仿函数
class AscendingSort {
public:
    template<typename T>
    bool operator()(const T& a, const T& b) const {
        return a < b;
    }
};

class DescendingSort {
public:
    template<typename T>
    bool operator()(const T& a, const T& b) const {
        return a > b;
    }
};

class AbsoluteValueSort {
public:
    bool operator()(int a, int b) const {
        return abs(a) < abs(b);
    }
};

template<typename SortStrategy>
void sortWithStrategy(vector<int>& data, SortStrategy strategy) {
    sort(data.begin(), data.end(), strategy);
}

int main() {
    vector<int> numbers = {-5, 3, -1, 4, -2};
    
    // 使用不同的排序策略
    sortWithStrategy(numbers, AscendingSort());
    // numbers: -5, -2, -1, 3, 4
    
    sortWithStrategy(numbers, DescendingSort());
    // numbers: 4, 3, -1, -2, -5
    
    sortWithStrategy(numbers, AbsoluteValueSort());
    // numbers: -1, -2, 3, 4, -5
    
    return 0;
}

仿函数 vs Lambda表达式

cpp

复制代码
void compareWithLambda() {
    vector<int> nums = {1, 2, 3, 4, 5, 6};
    
    // 仿函数方式
    class IsEvenFunctor {
    public:
        bool operator()(int x) const { return x % 2 == 0; }
    };
    
    // Lambda方式(本质是匿名仿函数)
    auto isEvenLambda = [](int x) { return x % 2 == 0; };
    
    int count1 = count_if(nums.begin(), nums.end(), IsEvenFunctor());
    int count2 = count_if(nums.begin(), nums.end(), isEvenLambda);
    int count3 = count_if(nums.begin(), nums.end(), 
                         [](int x) { return x % 2 == 0; }); // 直接使用
    
    cout << "偶数个数: " << count1 << endl;  // 3
    cout << "偶数个数: " << count2 << endl;  // 3
    cout << "偶数个数: " << count3 << endl;  // 3
}

性能优势

仿函数的性能优势主要来自:

  1. 内联优化:编译器可以轻松内联仿函数的调用

  2. 避免函数指针:模板实例化时类型信息完整

  3. 静态多态:编译期决议,无运行时开销

cpp

复制代码
// 仿函数 - 可以被内联
class SquareFunctor {
public:
    int operator()(int x) const { return x * x; }
};

// 函数指针 - 较难内联
int squareFunction(int x) { return x * x; }

template<typename Func>
void benchmark(const string& name, Func func) {
    auto start = chrono::high_resolution_clock::now();
    
    int result = 0;
    for (int i = 0; i < 1000000; ++i) {
        result += func(i);
    }
    
    auto end = chrono::high_resolution_clock::now();
    auto duration = chrono::duration_cast<chrono::microseconds>(end - start);
    
    cout << name << " 耗时: " << duration.count() << " 微秒" << endl;
}

int main() {
    benchmark("仿函数", SquareFunctor());
    benchmark("函数指针", squareFunction);
    benchmark("Lambda", [](int x) { return x * x; });
    
    return 0;
}

总结

仿函数是C++泛型编程的基石,它们提供了:

  • 状态保持:可以在调用之间保存信息

  • 高性能:编译器可以很好地进行优化

  • 灵活性:可以通过模板参数定制行为

  • 类型安全:编译期类型检查

  • 可组合性:可以创建复杂的操作组合

掌握仿函数对于理解STL设计理念和编写高效的C++代码至关重要!

相关推荐
草莓熊Lotso2 小时前
《算法闯关指南:优选算法--模拟》--39.替换所有问号,40.提莫攻击
开发语言·c++·算法·模拟
IT阳晨。2 小时前
【神经网络与深度学习(吴恩达)】深度学习概论学习笔记
笔记·深度学习·神经网络
艾莉丝努力练剑3 小时前
【Linux基础开发工具 (三)】Vim从入门到精通(下):效率翻倍的编辑技巧与个性化配置攻略
linux·运维·服务器·c++·ubuntu·centos·vim
草莓熊Lotso3 小时前
C++ STL set 系列完全指南:从底层原理、核心接口到实战场景
开发语言·c++·人工智能·经验分享·网络协议·算法·dubbo
咖啡の猫5 小时前
搭建Python开发环境
开发语言·python
程序猿小蒜6 小时前
基于springboot的共享汽车管理系统开发与设计
java·开发语言·spring boot·后端·spring·汽车
听风吟丶7 小时前
Java 8 Stream API 高级实战:从数据处理到性能优化的深度解析
开发语言·python
hygge9998 小时前
Spring Boot + MyBatis 整合与 MyBatis 原理全解析
java·开发语言·经验分享·spring boot·后端·mybatis
AA陈超8 小时前
ASC学习笔记0014:手动添加一个新的属性集
c++·笔记·学习·ue5