C++中的回調函數

1.普通函數作為回調函數

#include <iostream>void programA_FunA1() { printf("I'am ProgramA_FunA1 and be called..\n"); }void programA_FunA2() { printf("I'am ProgramA_FunA2 and be called..\n"); }void programB_FunB1(void (*callback)()) {printf("I'am programB_FunB1 and be called..\n");callback();
}int main(int argc, char **argv) {programA_FunA1();programB_FunB1(programA_FunA2);
}

2 .類的靜態函數作為回調函數

#include <iostream>class ProgramA {public:void FunA1() { printf("I'am ProgramA.FunA1() and be called..\n"); }static void FunA2() { printf("I'am ProgramA.FunA2() and be called..\n"); }
};class ProgramB {public:void FunB1(void (*callback)()) {printf("I'am ProgramB.FunB1() and be called..\n");callback();}
};int main(int argc, char **argv) {ProgramA PA;PA.FunA1();ProgramB PB;PB.FunB1(ProgramA::FunA2);
}
//缺點:static 函數不能訪問非static 成員變量或函數,會嚴重限制回調函數可以實現的功能。

3.?類的非靜態函數作為回調函數

#include <iostream>class ProgramA {public:void FunA1() { printf("I'am ProgramA.FunA1() and be called..\n"); }void FunA2() { printf("I'am ProgramA.FunA2() and be called..\n"); }
};class ProgramB {public:void FunB1(void (ProgramA::*callback)(), void *context) {printf("I'am ProgramB.FunB1() and be called..\n");((ProgramA *)context->*callback)();}
};int main(int argc, char **argv) {ProgramA PA;PA.FunA1();ProgramB PB;PB.FunB1(&ProgramA::FunA2, &PA);  // 此處都要加&
}

這種方法可以得到預期的結果,看似完美,但是也存在明顯不足。
比如在programB中FunB1還使用 programA的類型,也就我預先還要知道回調函數所屬的類定義,當programB想獨立封裝時就不好用了。
?

這里還有一種方法可以避免這樣的問題,可以把非static的回調函數 包裝為另一個static函數,這種方式也是一種應用比較廣的方法。
?

#include <iostream>class ProgramA {public:void FunA1() { printf("I'am ProgramA.FunA1() and be called..\n"); }void FunA2() { printf("I'am ProgramA.FunA2() and be called..\n"); }static void FunA2Wrapper(void *context) {printf("I'am ProgramA.FunA2Wrapper() and be called..\n");((ProgramA *)context)->FunA2();  // 此處調用的FunA2()是context的函數, 不是this->FunA2()}
};class ProgramB {public:void FunB1(void (ProgramA::*callback)(), void *context) {printf("I'am ProgramB.FunB1() and be called..\n");((ProgramA *)context->*callback)();}void FunB2(void (*callback)(void *), void *context) {printf("I'am ProgramB.FunB2() and be called..\n");callback(context);}
};int main(int argc, char **argv) {ProgramA PA;PA.FunA1();ProgramB PB;PB.FunB1(&ProgramA::FunA2, &PA);  // 此處都要加&printf("\n");PB.FunB2(ProgramA::FunA2Wrapper, &PA);
}

這種方法相對于上一種,ProgramB中沒有ProgramA的任何信息了,是一種更獨立的實現方式。
FunB2()通過調用FunA2Wrapper(),實現間接的對FunA2()的調用。FunA2()可以訪問和調用對類內的任何函數和變量。多了一個wrapper函數,也多了一些靈活性。
?

上面借助wrapper函數實現回調,雖然很靈活,但是還是不夠優秀,比如:
1)多了一個不是太有實際用處的wrapper函數。
2)wrapper中還要對傳入的指針進行強制轉換。
3)FunB2調用時,不但要指定wrapper函數的地址,還要傳入PA的地址。
還有更靈活、直接的方式。

std::funtion和std::bind可以登場了。
std::function是一種通用、多態的函數封裝。std::function的實例可以對任何可以調用的目標實體進行存儲、復制、和調用操作,這些目標實體包括普通函數、Lambda表達式、函數指針、以及其它函數對象等[1]。
std::bind()函數的意義就像它的函數名一樣,是用來綁定函數調用的某些參數的[2]。
?

#include <iostream>#include <functional> // fucntion/bindclass ProgramA {public:void FunA1() { printf("I'am ProgramA.FunA1() and be called..\n"); }void FunA2() { printf("I'am ProgramA.FunA2() and be called..\n"); }static void FunA3() { printf("I'am ProgramA.FunA3() and be called..\n"); }
};class ProgramB {typedef std::function<void ()> CallbackFun;public:void FunB1(CallbackFun callback) {printf("I'am ProgramB.FunB2() and be called..\n");callback();}
};void normFun() { printf("I'am normFun() and be called..\n"); }int main(int argc, char **argv) {ProgramA PA;PA.FunA1();printf("\n");ProgramB PB;PB.FunB1(normFun);printf("\n");PB.FunB1(ProgramA::FunA3);printf("\n");PB.FunB1(std::bind(&ProgramA::FunA2, &PA));
}

簡而言之,std::funtion是定義函數類型(輸入、輸出),std::bind是綁定特定的函數(具體的要調用的函數)。
?

相比于wrapper方法,這個方式要更直接、簡潔很多。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/35590.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/35590.shtml
英文地址,請注明出處:http://en.pswp.cn/news/35590.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

年之年的選擇,組裝版

組件&#xff1a;<!--* Author: liuyu liuyuxizhengtech.com* Date: 2023-02-01 16:57:27* LastEditors: wangping wangpingxizhengtech.com* LastEditTime: 2023-06-30 17:25:14* Description: 時間選擇年 - 年 --> <template><div class"year-range-pick…

04.利用Redis國邏輯過期實現緩存功能---解決緩存擊穿

學習目標&#xff1a; 提示&#xff1a;學習如何利用Redis邏輯過期實現添加緩存功能解決緩存擊穿 學習產出&#xff1a; 緩存擊穿講解圖&#xff1a; 解決方案&#xff1a; 采用互斥鎖采用邏輯過期 1. 準備pom環境 <dependency><groupId>org.springframework…

⑤ Axios網絡請求

Axios安裝 cnpm install --save axios post請求需要用到的&#xff1a; cnpm install --save querystring(用來轉換格式的) 引入 一般是全局引入&#xff0c;在main.js中引入 全局引入后的get和post方式使用 get請求方式 post請求方式 先引入&#xff1a; axios封裝…

STM32 定時器自動重裝載寄存器ARR帶來的影響,ARPE0和1區別

ARR是啥 自動重載寄存器是預裝載的。對自動重載寄存器執行寫入或讀取操作時會訪問預裝載寄存器。預裝載寄存器的內容既可以直接傳送到影子寄存器&#xff0c;也可以在每次發生更新事件 (UEV) 時傳送到影子寄存器&#xff0c;這取決于 TIMx_CR1 寄存器中的自動重載預裝載使能位 …

Go和Java實現中介者模式

Go和Java實現中介者模式 下面通過一個同事之間相互通信的例子來說明中介者模式的使用。 1、中介者模式 中介者模式是用來降低多個對象和類之間的通信復雜性。這種模式提供了一個中介類&#xff0c;該類通常處理不同類之間的 通信&#xff0c;并支持松耦合&#xff0c;使代碼…

1.作用域

1.1局部作用域 局部作用域分為函數作用域和塊作用域。 1.函數作用域: 在函數內部聲明的變量只能在函數內部被訪問&#xff0c;外部無法直接訪問。 總結&#xff1a; (1)函數內部聲明的變量&#xff0c;在函數外部無法被訪問 (2)函數的參數也是函數內部的局部變量 (3)不同函數…

Redis——三個特殊的數據類型+事務

概述 全稱為遠程字典服務。 Redis——基礎篇(包含redis在云服務上的docker化安裝和連接以及常用命令)_連接docker中的redis_北嶺山腳鼠鼠的博客-CSDN博客 Redis能干什么&#xff1f; 1.內存存儲、持久化&#xff0c;內存中是斷電即失&#xff0c;因此持久化很重要&#xff…

【LVS-NAT配置】

配置 node1&#xff1a;128&#xff08;客戶端&#xff09; node2&#xff1a;135&#xff08;調度器&#xff09; RS&#xff1a; node3&#xff1a;130 node4&#xff1a;132 node2添加網絡適配器&#xff08;僅主機模式&#xff09; [rootnode2 ~]# nmtui[rootnode2 ~]#…

ETLCloud+MaxCompute實現云數據倉庫的高效實時同步

MaxCompute介紹 MaxCompute是適用于數據分析場景的企業級SaaS&#xff08;Software as a Service&#xff09;模式云數據倉庫&#xff0c;以Serverless架構提供快速、全托管的在線數據倉庫服務&#xff0c;消除了傳統數據平臺在資源擴展性和彈性方面的限制&#xff0c;最小化用…

歐拉公式之證明

首先&#xff0c;我們考慮復數函數的泰勒級數展開式。對于任意一個復數函數f(z)&#xff0c;我們可以將其在za處進行泰勒級數展開&#xff1a; f(z) f(a) f(a)(z-a) f(a)(z-a)^2/2! f(a)(z-a)^3/3! ... 其中f(a)表示f(z)在za處的導數&#xff0c;f(a)表示f(z)在…

從零開始學Python(三)函數與lambda表達式

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下來看看由輝輝所寫的關于Python的相關操作吧 目錄 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 一.函數 1.函數與方法的區別 2.函數的定…

[PaddlePaddle] [學習筆記] [上] 計算機視覺(卷積、卷積核、卷積計算、padding計算、BN、縮放、平移、Dropout)

1. 計算機視覺的發展歷程 計算機視覺作為一門讓機器學會如何去“看”的學科&#xff0c;具體的說&#xff0c;就是讓機器去識別攝像機拍攝的圖片或視頻中的物體&#xff0c;檢測出物體所在的位置&#xff0c;并對目標物體進行跟蹤&#xff0c;從而理解并描述出圖片或視頻里的場…

深入解析 SOCKS5 代理及其在網絡安全與爬蟲中的應用

在當今數字化時代&#xff0c;網絡安全和數據獲取成為了互聯網時代的重要課題。為了實現安全的網絡連接和高效的數據采集&#xff0c;各種代理技術應運而生。本文將深入探討 SOCKS5 代理及其在網絡安全和爬蟲領域的應用&#xff0c;同時比較其與其他代理方式的優勢與劣勢。 1.…

TypeScript 語法

環境搭建 以javascript為基礎構建的語言&#xff0c;一個js的超集&#xff0c;可以在任何支持js的平臺中執行&#xff0c;ts擴展了js并且添加了類型&#xff0c;但是ts不能被js解析器直接執行&#xff0c;需要編譯器編譯為js文件&#xff0c;然后引入到 html 頁面使用。 ts增…

04 mysql innodb record

前言 最近看到了 何登成 大佬的 "深入MySQL源碼 -- Step By Step" 的 pdf 呵呵 似乎是找到了一些 方向 之前對于 mysql 方面的東西, 更多的僅僅是簡單的使用[業務中的各種增刪改查], 以及一些面試題的背誦 這里會參照 MySQL Internals Manual 來大致的看一下 i…

通過matlab對比music,mvdr以及tdoa三種定位算法的性能

目錄 1.算法運行效果圖預覽 2.算法運行軟件版本 3.部分核心程序 4.算法理論概述 4.1 MUSIC&#xff08;Multiple Signal Classification&#xff09; 4.2 MVDR&#xff08;Minimum Variance Distortionless Response&#xff09; 4.3 TDOA&#xff08;Time Difference o…

開發一個RISC-V上的操作系統(七)—— 硬件定時器(Hardware Timer)

目錄 往期文章傳送門 一、硬件定時器 硬件實現 軟件實現 二、上板測試 往期文章傳送門 開發一個RISC-V上的操作系統&#xff08;一&#xff09;—— 環境搭建_riscv開發環境_Patarw_Li的博客-CSDN博客 開發一個RISC-V上的操作系統&#xff08;二&#xff09;—— 系統引導…

AD域機器KMS自動激活

1、打開AD域控&#xff0c;點擊DNS管理 2、創建其它記錄 3、選擇服務位置 SRV 4、輸入相關信息 服務&#xff1a;_VLMCS協議&#xff1a;_TCP權重&#xff1a;100端口號&#xff1a;1688KMS服務器地址&#xff1a;10.3.0.211 5、成功&#xff0c;這時域內主機重啟后&#xff0…

騰訊云CVM服務器2核2g1m帶寬支持多少人訪問?

騰訊云2核2g1m的服務器支持多少人同時訪問&#xff1f;2核2g1m云服務器短板是在1M公網帶寬上&#xff0c;騰訊云服務器網以網站應用為例&#xff0c;當大規模用戶同時訪問網站時&#xff0c;很大概率會卡在公網帶寬上&#xff0c;所以壓根就談不上2核2G的CPU內存計算性能是否夠…

sql 執行的順序

在執行 SQL 查詢時&#xff0c;通常會按照以下順序進行處理&#xff1a; FROM 子句&#xff1a;指定要查詢的表或視圖。WHERE 子句&#xff1a;篩選滿足特定條件的行。GROUP BY 子句&#xff1a;將結果按照指定的列進行分組。HAVING 子句&#xff1a;篩選滿足特定條件的分組。…