博主介绍:程序喵大人

  • 35 - 资深C/C++/Rust/Android/iOS客户端开发
  • 10年大厂工作经验
  • 嵌入式/人工智能/自动驾驶/音视频/游戏开发入门级选手
  • 《C++20高级编程》《C++23高级编程》等多本书籍著译者
  • 更多原创精品文章,首发gzh,见文末
  • 👇👇记得订阅专栏,以防走丢👇👇
    😉C++基础系列专栏
    😃C语言基础系列专栏
    🤣C++大佬养成攻略专栏
    🤓C++训练营
    👉🏻个人网站

你有没有遇到过这种场景?

写回调函数时,纠结到底该用“函数指针”还是“lambda”?又或者,看到 C++ STL 里频繁出现的“函数对象(仿函数)”,忍不住一脸懵圈:这仨玩意儿,真的有那么多区别吗?

今天,我们就来一口气讲清楚这三个在 C++ 中常见的“可调用对象”,不仅要分清它们的语法差异,更要搞懂 它们背后的性能差异实际应用建议

一、三个概念先讲清

✅ 函数指针(Function Pointer)

最传统的调用方式,C语言遗产。

void say_hello() {std::cout << "Hello!\n";
}void call(void (*func)()) {func(); // 函数指针调用
}

适合传递普通函数,语法较繁琐,对类型要求严格,不支持捕获外部变量。

✅ 函数对象 / 仿函数(Function Object)

本质是一个“重载了 operator() 的类”,可以像函数一样使用对象。

struct Adder {int operator()(int a, int b) const {return a + b;}
};

优点是可携带状态、可内联优化,STL 算法中大量使用,比如 std::sort 搭配比较器。

✅ Lambda 表达式

C++11 后的香饽饽,本质是一个匿名的函数对象,写法灵活、可捕获变量。

auto adder = [](int a, int b) { return a + b; };

既能像函数指针那样使用,又能像函数对象一样携带状态,兼具两者优点。

二、核心问题:哪个性能更高?

结论先行:

函数对象 ≈ lambda > 函数指针 > std::function

是不是有点出乎意料?我们一个个讲。

1. 函数对象 vs lambda:几乎打平

因为 lambda 本质就是编译器帮你生成的匿名函数对象,它们都是 编译期类型、可以被 内联优化

举个例子:
#include <algorithm>
#include <vector>std::vector<int> vec = {3, 1, 4, 1, 5};std::sort(vec.begin(), vec.end(), [](int a, int b) {return a > b;
});

这个 lambda 表达式,最终会被编译器转成类似如下结构:

struct Comp {bool operator()(int a, int b) const { return a > b; }
};

也就是说,从性能角度来看,lambda 和你手写的函数对象效果是一样的,区别只是有没有名字而已。

优势:可内联优化、零额外开销
劣势:略显抽象,捕获变量时可能造成误用(比如引用捕获生命周期问题)

2. 函数指针:灵活但“冷门”

函数指针因为是 运行时确定的函数地址,所以不能内联,性能略差。

void foo() { std::cout << "Hello\n"; }
void run(void (*fp)()) { fp(); }

相比函数对象或 lambda,它的开销略高,主要体现在:

  • 无法内联 → 函数调用成本更高
  • 不能携带状态 → 扩展性差
  • 类型不灵活 → 泛型编程不友好

但它依然有用武之地,比如你要调用某个库函数的钩子、处理 C 风格 API(如 qsort)时,函数指针是必须的。

3. std::function:最灵活也最慢

std::function 是一个 类型擦除容器,可以包装任意可调用对象(包括函数指针、lambda、仿函数等),代价是:

  • 要在堆上分配空间(如果可调用对象太大)
  • 无法内联
  • 性能开销比前三者都大
std::function<void()> func = [] { std::cout << "Hello\n"; };

建议在 必须多态传参统一接口 场景下使用,其他场景谨慎上。

三、实际开发怎么选?

场景推荐使用原因
STL 算法排序、查找等lambda / 仿函数编译期优化,零开销
回调函数 / 钩子传参函数指针简洁直观
状态携带、灵活封装lambda / 仿函数可维护性强
多态可调用对象封装std::function提高通用性,牺牲性能

总结:不要为了“酷”而用 lambda

虽然 lambda 是现代 C++ 的明星,但它并非万能:

  • 如果你只需要传个裸函数地址,用函数指针更轻量;
  • 如果你需要封装复杂逻辑,lambda、仿函数才是首选;
  • 如果你想要灵活接口、动态传参,那就老老实实用 std::function 吧。

💡最重要的是:理解每种可调用对象的代价和场景,才是“高效”的真谛。

码字不易,欢迎大家点赞,关注,评论,谢谢!

👉 C++训练营

一个专为校招、社招3年工作经验的同学打造的 1v1 项目实战训练营,量身定制学习计划、每日代码review,简历优化,面试辅导,已帮助多名学员获得大厂offer!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/93731.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/93731.shtml
英文地址,请注明出处:http://en.pswp.cn/web/93731.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java团队项目开发规范——对象分层规范

分层与对象命名规范如上图所示&#xff0c;系统划分成3个层&#xff1a;Controller层&#xff0c;Service层&#xff0c;Domain层 Controller层&#xff1a; Controller层是接入层&#xff0c;提供对外或者前端的接口&#xff0c;该层主要作用是提供对外接口的封装。基于CQRS分…

低功耗模式

1. 什么是低功耗&#xff1f;低功耗模式&#xff1a;MCU 暂停部分时钟/外设&#xff0c;降低电流消耗&#xff0c;等待外部事件&#xff08;中断/复位/唤醒&#xff09;再恢复运行。应用场景&#xff1a;电池供电设备&#xff08;传感器、手持设备、IoT 节点&#xff09;——延…

GPT-5 官方前瞻:它将如何重塑你的数字生活?

你是否曾想过&#xff0c;有一天你的浏览器不再是一个被动等待指令的工具&#xff0c;而是一个能主动为你分忧解难的智能伙伴&#xff1f;OpenAI 的 CEO Sam Altman 最近的发言&#xff0c;以及关于 GPT-5 的种种迹象&#xff0c;都预示着这个未来比我们想象的更近。这不仅是一…

驱动开发系列65 - NVIDIA 开源GPU驱动open-gpu-kernel-modules 目录结构

一:OS相关部分 kernel-open/ 内核接口层 kernel-open/nvidia/ nvidia.ko 的接口层,负责GPU初始化,显存管理,PCIe通信,中断处理,电源管理等底层功能。 kernel-open/nvidia-drm/ nvidia-drm.ko 的接口层,提供标准图形接口,让Xorg、Wayland、Kwin、GNOME等桌面环境能够通…

GPT-4.1旗舰模型:复杂任务的最佳选择及API集成实践

GPT-4.1旗舰模型&#xff1a;复杂任务的最佳选择及API集成实践 概述 GPT-4.1作为新一代旗舰大模型&#xff0c;凭借其卓越的智能表现、强大的跨领域问题解决能力&#xff0c;成为复杂任务处理的首选。本文将详细解析GPT-4.1的核心能力、接口用法、计费方式、功能对比及API集成…

paimon保姆级教程简介

还在纠结 Flink 配 Hudi 还是 Iceberg&#xff1f;别选了&#xff0c;快来试试 Flink 的“天选之子”—— Apache Paimon&#xff01; 忘掉复杂的 Lambda 架构&#xff0c;拥抱真正的流批一体。我们的 Paimon 视频教程&#xff0c;带你用 Flink 原生湖仓格式&#xff0c;轻松构…

Transformer中的编码器和解码器是什么?

今天&#xff0c;我们来具体介绍Transformer的架构设计。 一个完整的Transformer模型就像一个高效的语言处理工厂&#xff0c;主要由两大车间组成&#xff1a;编码车间和解码车间。 首先来看这幅“世界名画”&#xff0c;你可以在介绍Transformer的场景中常常看到这幅图&#x…

uniapp 应用未安装:软件包与现有软件包存在冲突

应用未安装&#xff1a;软件包与现有软件包存在冲突常见原因包名&#xff08;AppID&#xff09;没变&#xff0c;但签名证书不同安卓会把同一包名的 App 当成同一个应用。如果你之前安装的版本用了 A 签名&#xff0c;现在你打包用了 B 签名&#xff0c;就会冲突&#xff0c;导…

MyCAT2的主从配置

1.创建数据源重置配置&#xff1a;/* mycat:resetConfig{} */添加读写的数据源/* mycat:createDataSource {"dbType": "mysql","idleTimeout": 60000,"initSqls": [],"initSqlsGetConnection": true,"instanceType&quo…

个人介绍CSDNmjhcsp

年龄&#xff1a;12岁 住址&#xff1a;山东潍坊 看的这&#xff0c;有人懵了&#xff0c;访问量4.8万的mjhcsp竟然是一个小孩&#xff01; 好吧&#xff0c;我的强项其实是C&#xff0c;但是C发表文章很少&#xff0c;我平常写一写java&#xff0c;云原生&#xff0c;Deeps…

01-Docker-简介、安装与使用

1. docker简介 Docker 是一个应用打包、分发、部署的工具你也可以把它理解为一个轻量的虚拟机&#xff0c;它只虚拟你软件需要的运行环境&#xff0c;多余的一点都不要&#xff0c;而普通虚拟机则是一个完整而庞大的系统&#xff0c;包含各种不管你要不要的软件。 2. 相关概念 …

阿里云参数配置化

阿里云参数配置化 一、问题描述 当我们直接在AliOSSUtils.java中对所需的阿里云OSS相关参数进行赋值时&#xff0c;当相关参数发生改变&#xff0c;但是又在多次进行了赋值这些参数&#xff0c;那么就需要逐一进行修改&#xff0c;所以我们直接在SpringBoot项目的配置文件appli…

Diamond开发经验(1)

前言: 学习Lattice的芯片开发的过程中&#xff0c;很多实际开发过程中遇到的问题是没办法绕过的&#xff0c;虽然我今天被绕了一天&#xff08;此句多余&#xff0c;单纯记录美好心情哈哈哈哈&#xff09;将这些解决方法梳理成文章供大家参考&#xff0c;十个问题组成一篇文章。…

神经网络训练过程详解

神经网络训练过程详解 神经网络训练过程是一个动态的、迭代的学习过程&#xff0c;接下来基于一段代码展示模型是如何逐步学习数据规律的。 神经网络拟合二次函数&#xff1a;代码详解 下面将详细解释这段代码&#xff0c;它使用神经网络拟合一个带有噪声的二次函数 y x 2x …

LeetCode100-560和为K的子数组

本文基于各个大佬的文章上点关注下点赞&#xff0c;明天一定更灿烂&#xff01;前言Python基础好像会了又好像没会&#xff0c;所有我直接开始刷leetcode一边抄样例代码一边学习吧。本系列文章用来记录学习中的思考&#xff0c;写给自己看的&#xff0c;也欢迎大家在评论区指导…

【PZ-ZU47DR-KFB】璞致FPGA ZYNQ UltraScalePlus RFSOC QSPI Flash 固化常见问题说明

1 Flash 固化Flash 固化需要先生成 BOOT.bin 文件&#xff0c;这边以裸机的串口工程进行讲解如何生成 BOOT.bin 文件及 Flash 固化操作。有读者会遇到&#xff0c;只使用 PL 端的情况&#xff0c;也需要进行 Flash 固化。我们需要添加 PS 端最小配置&#xff08;包含 Flash 配置…

数据结构:查找表

一、数据结构的概念数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。它不仅仅是存储数据的方式&#xff0c;更强调数据之间的逻辑关系和操作方法。数据结构主要从以下几个角度来理解&#xff1a;1. 数据之间的关系逻辑结构&#xff1a;集合结构&#xff1a;元素之…

自建知识库,向量数据库 (十)之 文本向量化——仙盟创梦IDE

自建文章向量化技术&#xff1a;AI 浪潮下初学者的进阶指南 在人工智能&#xff08;AI&#xff09;蓬勃发展的浪潮中&#xff0c;向量化作为将文本数据转化为数值向量表示的关键技术&#xff0c;成为理解和处理文本的基石。本文将结合给定的代码示例&#xff0c;深入探讨自建文…

数据结构 -- 顺序表的特点、操作函数

线性表顺序存储的优缺点优点无需为表中的逻辑关系增加额外的存储空间&#xff0c;利用连续的内存单元存储数据&#xff0c;存储密度高。支持 随机访问&#xff0c;通过下标可在 O(1) 时间复杂度内定位元素&#xff08;如数组按索引取值&#xff09;&#xff0c;查询效率稳定。缺…

反向代理实现服务器联网

下载脚本&#xff1a;https://gitee.com/995770513/ssh-reverse-socket然后解压到 D:\Download在本机运行 cd D:\Download\ssh-reverse-socket-master\ssh-reverse-socket-master python socket5_proxy.py --ssh_cmd "xaserver10.150.10.51 -p 22" --socket5_port 78…