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

相关推荐
似水明俊德14 小时前
02-C#.Net-反射-面试题
开发语言·面试·职场和发展·c#·.net
无极低码14 小时前
ecGlypher新手安装分步指南(标准化流程)
人工智能·算法·自然语言处理·大模型·rag
软件算法开发15 小时前
基于海象优化算法的LSTM网络模型(WOA-LSTM)的一维时间序列预测matlab仿真
算法·matlab·lstm·一维时间序列预测·woa-lstm·海象优化
Thera77715 小时前
C++ 高性能时间轮定时器:从单例设计到 Linux timerfd 深度优化
linux·开发语言·c++
superior tigre15 小时前
22 括号生成
算法·深度优先
炘爚16 小时前
C语言(文件操作)
c语言·开发语言
阿蒙Amon16 小时前
C#常用类库-详解SerialPort
开发语言·c#
凸头16 小时前
CompletableFuture 与 Future 对比与实战示例
java·开发语言
wuqingshun31415916 小时前
线程安全需要保证几个基本特征
java·开发语言·jvm
君义_noip16 小时前
信息学奥赛一本通 1952:【10NOIP普及组】三国游戏 | 洛谷 P1199 [NOIP 2010 普及组] 三国游戏
c++·信息学奥赛·csp-s