通过示例看 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++ 编程中的重要工具。

相关推荐
99乘法口诀万物皆可变20 小时前
PcanToVectorXL_V01:打通 Vector 与 PCAN 的双向 CAN/CAN‑FD 桥梁
c++·学习
可编程芯片开发20 小时前
基于VSG虚拟同步发电机控制的三相并网逆变器带多组可变负载Simulink建模与仿真
算法
摇滚侠20 小时前
方法 A 等方法 B 执行完再执行 叫同步调用还是异步调用 JS 默认是同步调用还是异步调用
开发语言·javascript·ecmascript
AI服务老曹20 小时前
国产NPU视觉算法参数配置说明
算法·性能优化·边缘计算
liulun20 小时前
C++ WinRT中的事件
开发语言·c++
彦为君20 小时前
Redis最新版本特性
java·数据库·redis·算法·bootstrap
whitelbwwww21 小时前
c++运行onnx模型
开发语言·c++
码来的小朋友21 小时前
手把手教你用 Python + PyQt5 做一个可视化图片切图工具
开发语言·python·microsoft
触底反弹21 小时前
🔥 字符串算法面试三连击:反转、回文、回文变种,搞懂这三题稳了!
前端·javascript·算法
aaaameliaaa21 小时前
计算斐波那契数(递归、迭代)(1,1,2,3,5.....)
c语言·开发语言·笔记·算法·排序算法