函數對象 vs 函數指針 vs lambda:該用哪個才高效?

博主介紹:程序喵大人

  • 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…