通过示例看 C++ 函数对象、仿函数、operator( )

operator() 在 C++ 中被称为 函数调用运算符 ,它允许类的对象像函数一样被调用。这种对象被称为 函数对象仿函数

一、基本语法

cpp 复制代码
class MyFunctor {
public:
    // 重载 () 运算符
    return_type operator()(parameters) {
        // 实现代码
    }
};

// 使用
MyFunctor functor;
functor(arg1, arg2);  // 像函数一样调用

二、基础示例

示例 1:简单的仿函数

cpp 复制代码
#include <iostream>

class Adder {
private:
    int value;
public:
    Adder(int v) : value(v) {}
    
    // 重载 () 运算符
    int operator()(int x) {
        return value + x;
    }
};

int main() {
    Adder add5(5);
    Adder add10(10);
    
    std::cout << add5(3) << std::endl;   // 输出: 8
    std::cout << add5(10) << std::endl;  // 输出: 15
    std::cout << add10(3) << std::endl;  // 输出: 13
    
    return 0;
}

示例 2:带状态的计数器

cpp 复制代码
#include <iostream>

class Counter {
private:
    int count;
public:
    Counter() : count(0) {}
    
    int operator()() {
        return ++count;
    }
    
    int operator()(int increment) {
        count += increment;
        return count;
    }
    
    void reset() { count = 0; }
};

int main() {
    Counter counter;
    
    std::cout << counter() << std::endl;  // 输出: 1
    std::cout << counter() << std::endl;  // 输出: 2
    std::cout << counter(5) << std::endl; // 输出: 7
    counter.reset();
    std::cout << counter() << std::endl;  // 输出: 1
    
    return 0;
}

三、在 STL 算法中的应用

operator() 在 STL 中非常有用,特别是与算法配合使用:

示例 3:自定义比较器

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

class CompareByLength {
public:
    bool operator()(const std::string& a, const std::string& b) const {
        return a.length() < b.length();
    }
};

class GreaterThan {
private:
    int threshold;
public:
    GreaterThan(int t) : threshold(t) {}
    
    bool operator()(int x) const {
        return x > threshold;
    }
};

int main() {
    // 1. 使用自定义比较器排序
    std::vector<std::string> words = {"apple", "banana", "cherry", "date"};
    std::sort(words.begin(), words.end(), CompareByLength());
    
    for (const auto& word : words) {
        std::cout << word << " ";  // 输出: date apple cherry banana
    }
    std::cout << std::endl;
    
    // 2. 使用自定义谓词
    std::vector<int> numbers = {1, 5, 10, 15, 20, 25};
    GreaterThan gt10(10);
    
    // 使用 find_if
    auto it = std::find_if(numbers.begin(), numbers.end(), gt10);
    if (it != numbers.end()) {
        std::cout << "First number > 10: " << *it << std::endl;  // 输出: 15
    }
    
    // 统计大于10的元素个数
    int count = std::count_if(numbers.begin(), numbers.end(), GreaterThan(10));
    std::cout << "Count > 10: " << count << std::endl;  // 输出: 3
    
    return 0;
}

四、Lambda 表达式与仿函数的关系

Lambda 表达式本质上是创建了一个匿名的仿函数类:

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

int main() {
    std::vector<int> numbers = {1, 2, 3, 4, 5};
    
    // Lambda 表达式
    auto lambda = [](int x) { return x * 2; };
    
    // 这等价于下面的仿函数
    class LambdaEquivalent {
    public:
        int operator()(int x) const {
            return x * 2;
        }
    };
    
    // 使用 lambda
    std::transform(numbers.begin(), numbers.end(), numbers.begin(), lambda);
    
    for (int n : numbers) {
        std::cout << n << " ";  // 输出: 2 4 6 8 10
    }
    std::cout << std::endl;
    
    return 0;
}

五、带捕获的 Lambda 对应的仿函数

cpp 复制代码
#include <iostream>

int main() {
    int factor = 3;
    
    // Lambda 表达式捕获外部变量
    auto lambda = [factor](int x) { return x * factor; };
    
    // 对应的仿函数实现
    class LambdaWithCapture {
    private:
        int factor;  // 捕获的变量作为成员
    public:
        LambdaWithCapture(int f) : factor(f) {}
        
        int operator()(int x) const {
            return x * factor;
        }
    };
    
    std::cout << lambda(5) << std::endl;  // 输出: 15
    
    LambdaWithCapture functor(factor);
    std::cout << functor(5) << std::endl;  // 输出: 15
    
    return 0;
}

六、高级应用示例

示例 4:函数适配器

cpp 复制代码
#include <iostream>
#include <functional>

// 自定义函数适配器:将二元函数转换为一元函数
template<typename Func, typename SecondArg>
class BindSecond {
private:
    Func func;
    SecondArg second;
public:
    BindSecond(Func f, SecondArg s) : func(f), second(s) {}
    
    template<typename FirstArg>
    auto operator()(FirstArg&& first) {
        return func(std::forward<FirstArg>(first), second);
    }
};

int multiply(int a, int b) {
    return a * b;
}

int main() {
    // 使用标准库的 bind
    using namespace std::placeholders;
    auto multiplyBy5 = std::bind(multiply, _1, 5);
    
    // 使用自定义的适配器
    BindSecond<decltype(&multiply), int> multiplyBy3(multiply, 3);
    
    std::cout << multiplyBy5(10) << std::endl;    // 输出: 50
    std::cout << multiplyBy3(10) << std::endl;    // 输出: 30
    
    return 0;
}

示例 5:记忆化(Memoization)

cpp 复制代码
#include <iostream>
#include <unordered_map>
#include <functional>

class Memoizer {
private:
    std::function<int(int)> func;
    std::unordered_map<int, int> cache;
    
public:
    Memoizer(std::function<int(int)> f) : func(f) {}
    
    int operator()(int n) {
        if (cache.find(n) != cache.end()) {
            std::cout << "Cache hit for " << n << std::endl;
            return cache[n];
        }
        
        std::cout << "Computing for " << n << std::endl;
        int result = func(n);
        cache[n] = result;
        return result;
    }
};

int expensiveComputation(int n) {
    // 模拟耗时计算
    return n * n;
}

int main() {
    Memoizer memo(expensiveComputation);
    
    std::cout << memo(5) << std::endl;  // 计算并缓存
    std::cout << memo(5) << std::endl;  // 从缓存读取
    std::cout << memo(10) << std::endl; // 计算并缓存
    std::cout << memo(10) << std::endl; // 从缓存读取
    
    return 0;
}

示例 6:访问者模式中的应用

cpp 复制代码
#include <iostream>
#include <vector>

class Circle;
class Square;

// 访问者基类
class ShapeVisitor {
public:
    virtual void operator()(Circle& circle) = 0;
    virtual void operator()(Square& square) = 0;
};

// 形状基类
class Shape {
public:
    virtual void accept(ShapeVisitor& visitor) = 0;
    virtual ~Shape() = default;
};

// 具体形状
class Circle : public Shape {
public:
    double radius;
    Circle(double r) : radius(r) {}
    
    void accept(ShapeVisitor& visitor) override {
        visitor(*this);
    }
};

class Square : public Shape {
public:
    double side;
    Square(double s) : side(s) {}
    
    void accept(ShapeVisitor& visitor) override {
        visitor(*this);
    }
};

// 具体访问者:面积计算器
class AreaCalculator : public ShapeVisitor {
public:
    double totalArea = 0;
    
    void operator()(Circle& circle) override {
        double area = 3.14159 * circle.radius * circle.radius;
        std::cout << "Circle area: " << area << std::endl;
        totalArea += area;
    }
    
    void operator()(Square& square) override {
        double area = square.side * square.side;
        std::cout << "Square area: " << area << std::endl;
        totalArea += area;
    }
};

int main() {
    std::vector<Shape*> shapes = {
        new Circle(5.0),
        new Square(4.0),
        new Circle(3.0)
    };
    
    AreaCalculator calculator;
    
    for (auto shape : shapes) {
        shape->accept(calculator);
    }
    
    std::cout << "Total area: " << calculator.totalArea << std::endl;
    
    // 清理
    for (auto shape : shapes) {
        delete shape;
    }
    
    return 0;
}

七、operator() 的优点

  1. 可维护状态 与普通函数不同,仿函数可以有成员变量,可以保存状态

  2. 可内联 编译器更容易内联 operator() 的调用,性能更好

  3. 可模板化 仿函数可以是模板类,提供更大的灵活性

  4. STL兼容 STL算法通常要求函数对象,使用 operator() 可以无缝集成

  5. 多态性 可以通过继承和虚函数实现多态行为

八、使用建议

  1. 对于简单的、一次性使用的函数对象,优先使用 Lambda

  2. 当需要复杂的状态管理、继承或多态时,使用显式的仿函数类

  3. 如果 operator() 不修改对象状态,声明为 const 成员函数

  4. 在模板化的 operator() 中使用完美转发

cpp 复制代码
class GenericFunctor {
public:
    template<typename T, typename U>
    auto operator()(T&& t, U&& u) const {
        return std::forward<T>(t) + std::forward<U>(u);
    }
};

operator() 是 C++ 中实现函数对象的核心机制,它提供了比普通函数指针更强大、更灵活的功能,是现代 C++ 编程中的重要工具。

相关推荐
元亓亓亓2 小时前
LeetCode热题100--139. 单词拆分--中等
算法·leetcode·职场和发展
kupeThinkPoem2 小时前
std::thread的使用
c++
leaves falling2 小时前
c语言将三个整数数按从大到小输出
c语言·开发语言
superman超哥2 小时前
仓颉高性能实践:内存布局优化技巧深度解析
c语言·开发语言·c++·python·仓颉
代码游侠2 小时前
学习笔记——数据封包拆包与协议
linux·运维·开发语言·网络·笔记·学习
释怀°Believe2 小时前
Daily算法刷题【面试经典150题-6️⃣kadane/】
算法·面试·职场和发展
薯片锅巴2 小时前
代码随想录算法训练营第四十四天:孤岛计数(广搜版),孤岛计数(深搜版),最大岛屿的面积
算法
海清河晏1112 小时前
Linux进阶篇:深入理解线程
java·jvm·算法
2301_797312262 小时前
学习Java32天
java·开发语言