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

相关推荐
汉克老师3 分钟前
GESP2025年3月认证C++五级( 第三部分编程题(2、原根判断))
c++·算法·模运算·gesp5级·gesp五级·原根·分解质因数
李崧正9 分钟前
Java技术分享:Lambda表达式与函数式编程
java·开发语言·python
老了,不知天命12 分钟前
鳶尾花項目JAVA
java·开发语言·机器学习
BIGmustang13 分钟前
python练手之用tkinter写一个计算器
开发语言·python
winner888121 分钟前
从零吃透C++命名空间、std、#include、string、vector
java·开发语言·c++
数据皮皮侠24 分钟前
上市公司创新韧性数据(2000-2024)|顶刊同款 EIR 指数
大数据·人工智能·算法·智慧城市·制造
WL_Aurora27 分钟前
Python 算法基础篇之链表
python·算法·链表
AI人工智能+电脑小能手29 分钟前
【大白话说Java面试题】【Java基础篇】第26题:Java的抽象类和接口有哪些区别
java·开发语言·面试
科研前沿35 分钟前
纯视觉无感解算 + 动态数字孪生:室内外无感定位技术全新升级
大数据·人工智能·算法·重构·空间计算
bzmK1DTbd38 分钟前
SOLID原则在Java中的实践:单一职责与开闭原则
java·开发语言·开闭原则