C++中std::functional 使用场景

 更新时间:2026年02月19日 10:19:16   作者:sTone87375  
std::functional是C++标准库中一个非常强大的工具,它提供了一种类型擦除机制,让你能够存储、传递和调用任何可调用对象,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

std::functional 是 C++ 标准库中一个非常强大的工具,它提供了一种**类型擦除(type erasure)**机制,让你能够存储、传递和调用任何可调用对象(callable)。

核心作用

1.统一的可调用对象包装器

std::function 可以包装任何可调用实体,只要签名匹配:

  • 普通函数
  • Lambda 表达式
  • 函数对象(仿函数)
  • 成员函数(通过 std::bind 或 lambda)
  • 甚至其他 std::function

2.类型擦除

隐藏具体类型,只暴露接口。这使得你可以:

  • 在容器中存储不同类型的可调用对象
  • 作为函数参数接受任意可调用对象
  • 实现回调机制而不需要模板

主要使用场景

场景 1:回调函数(Callbacks)

#include <functional>
#include <iostream>

class Button {
    std::function<void()> onClick_;
public:
    void setOnClick(std::function<void()> callback) {
        onClick_ = callback;
    }
    void click() { if (onClick_) onClick_(); }
};

// 使用
Button btn;
btn.setOnClick([]() { std::cout << "Clicked!\n"; });
btn.click();

场景 2:策略模式 / 算法注入

#include <functional>
#include <vector>
#include <algorithm>

void processData(std::vector<int>& data, 
                 std::function<bool(int)> filter,
                 std::function<int(int)> transform) {
    // 先过滤
    data.erase(std::remove_if(data.begin(), data.end(), 
              [&](int x) { return !filter(x); }), data.end());
    // 再转换
    for (auto& x : data) x = transform(x);
}

// 使用
std::vector<int> nums = {1, 2, 3, 4, 5, 6};
processData(nums,
    [](int x) { return x % 2 == 0; },  // 只保留偶数
    [](int x) { return x * x; }         // 平方
);

场景 3:事件系统 / 观察者模式

#include <functional>
#include <vector>
#include <string>

class EventSystem {
    std::vector<std::function<void(const std::string&)>> listeners_;
public:
    void subscribe(std::function<void(const std::string&)> listener) {
        listeners_.push_back(listener);
    }
    void emit(const std::string& event) {
        for (auto& listener : listeners_) {
            listener(event);
        }
    }
};

// 使用
EventSystem events;
events.subscribe([](const std::string& e) { 
    std::cout << "Logger: " << e << "\n"; 
});
events.subscribe([](const std::string& e) { 
    std::cout << "Metrics: recorded " << e << "\n"; 
});

场景 4:延迟执行 / 任务队列

#include <functional>
#include <queue>
#include <iostream>

class TaskQueue {
    std::queue<std::function<void()>> tasks_;
public:
    void addTask(std::function<void()> task) {
        tasks_.push(task);
    }
    void runAll() {
        while (!tasks_.empty()) {
            tasks_.front()();
            tasks_.pop();
        }
    }
};

// 使用
TaskQueue queue;
queue.addTask([]() { std::cout << "Task 1\n"; });
queue.addTask([]() { std::cout << "Task 2\n"; });
queue.runAll();

场景 5:绑定成员函数

#include <functional>
#include <iostream>

class Calculator {
public:
    int add(int a, int b) { return a + b; }
    int multiply(int a, int b) { return a * b; }
};

// 使用 std::bind
Calculator calc;
auto addFunc = std::bind(&Calculator::add, &calc, 
                         std::placeholders::_1, std::placeholders::_2);
std::cout << addFunc(3, 4); // 7

// 或者使用 lambda(更推荐,性能更好)
auto multiplyFunc = [&calc](int a, int b) { 
    return calc.multiply(a, b); 
};

std::functionvs 模板 vs 裸函数指针

特性std::function模板裸函数指针
类型擦除✅ 是❌ 否✅ 是(但只能指向函数)
存储 Lambda✅ 可以✅ 可以❌ 不行(除非无捕获)
运行时开销有(虚函数调用)
编译时类型检查弱(运行时可能抛 bad_function_call)
存储在容器中✅ 容易❌ 难(需要类型擦除)✅ 可以

最佳实践

  1. 优先使用模板:如果不需要存储可调用对象,用模板参数更高效

    // 更好:零开销
    template<typename Func>
    void execute(Func&& f) { f(); }
    
    // 有开销:类型擦除
    void execute(std::function<void()> f) { f(); }
    
  2. 检查空状态:调用前确保 std::function 不为空

    if (func) func();  // 或 if (func != nullptr)
    
  3. 注意开销std::function 通常使用小对象优化(SOO),但大对象会堆分配

  4. C++23 替代:考虑使用 std::move_only_function(仅移动)或 std::copyable_function

std::functional 的核心价值在于灵活性——当你需要在运行时决定调用什么、或者需要在容器中存储可调用对象时,它是不可或缺的工具。

到此这篇关于C++中std::functional 使用场景的文章就介绍到这了,更多相关C++ std::functional 使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++ 中RTTI的使用方法详解

    C++ 中RTTI的使用方法详解

    这篇文章主要介绍了C++ 中RTTI的使用方法详解的相关资料,希望通过本文大家能理解使用RTTI,需要的朋友可以参考下
    2017-09-09
  • C与C++中结构体的区别

    C与C++中结构体的区别

    C中的结构体只涉及到数据结构,而不涉及到算法,也就是说在C中数据结构和算法是分离的,而到C++中一类或者一个结构体可以包含函数(这个函数在C++我们通常中称为成员函数),C++中的结构体和类体现了数据结构和算法的结合
    2013-10-10
  • C++实现LeetCode(309.买股票的最佳时间含冷冻期)

    C++实现LeetCode(309.买股票的最佳时间含冷冻期)

    这篇文章主要介绍了C++实现LeetCode(309.买股票的最佳时间含冷冻期),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • 为什么获取环境变量getenv小心有坑

    为什么获取环境变量getenv小心有坑

    这篇文章主要介绍了获取环境变量getenv小心有坑问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-03-03
  • C++ 十进制转换为二进制的实例代码

    C++ 十进制转换为二进制的实例代码

    这篇文章介绍了C++ 十进制转换为二进制的实例代码,有需要的朋友可以参考一下
    2013-10-10
  • 使用C语言实现扫雷游戏

    使用C语言实现扫雷游戏

    这篇文章主要为大家详细介绍了使用C语言实现扫雷游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • C++实现循环队列

    C++实现循环队列

    这篇文章主要为大家详细介绍了C++实现循环队列,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-01-01
  • C++中的操作符重载详细解析

    C++中的操作符重载详细解析

    运算符重载后不能改变运算符的操作对象(操作数)的个数;如:"+"是实现两个操作数的运算符,重载后仍然为双目运算符
    2013-09-09
  • c++ chrono 获取当前时间的实现代码

    c++ chrono 获取当前时间的实现代码

    这篇文章主要介绍了c++ chrono 获取当前时间的实现代码,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-07-07
  • 使用C语言求解扑克牌的顺子及n个骰子的点数问题

    使用C语言求解扑克牌的顺子及n个骰子的点数问题

    这篇文章主要介绍了使用C语言求解扑克牌的顺子及n个骰子的点数问题的方法,解答实例主要为了突出解题的算法,需要的朋友可以参考下
    2016-03-03

最新评论