通过示例看 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 分钟前
Multisim14.0的详细中文安装步骤【附安装包】
c++
小白学大数据19 分钟前
超简单:用 Python 让 Excel 飞起来:用 openpyxl 把重复报表整理交给脚本
开发语言·数据库·python·excel
旧梦952730 分钟前
Java SortedMap 接口详解:从入门到实战
java·开发语言
莫陌尛.34 分钟前
Java_this构造方法
java·开发语言
Persistent的粽子!1 小时前
C++:类与对象(二)
开发语言·c++
Dreams°1231 小时前
【Java后端+Vue前后端分离:内网正常、公网访问异常|5个高频经典踩坑完整复盘】
java·开发语言·vue.js
Omics Pro1 小时前
Arc研究所Cell|全新虚拟细胞官方基准评测框架
大数据·人工智能·深度学习·算法·机器学习
蒸蒸yyyyzwd2 小时前
cpp 选手备战秋招学习笔记 day23
c++·求职招聘
.YM.Z2 小时前
C++ STL 容器深度剖析:vector 与 list 的模拟实现及对比
开发语言·c++·vector·list
lsylalalala2 小时前
多线程(4)
java·开发语言·多线程