C語(yǔ)言仿函數(shù)(Functor)實(shí)現(xiàn)示例
文檔版本:V1.0
適用場(chǎng)景:C語(yǔ)言開(kāi)發(fā)中需“帶狀態(tài)可調(diào)用對(duì)象”的場(chǎng)景(如自定義排序、計(jì)數(shù)器、帶配置的回調(diào)等)
前置知識(shí):C語(yǔ)言結(jié)構(gòu)體、函數(shù)指針、基本內(nèi)存操作
1. 概述
1.1 仿函數(shù)的本質(zhì)
仿函數(shù)(Functor)起源于C++,是重載operator()的類/對(duì)象,具備兩大核心特性:
- 可調(diào)用性:像函數(shù)一樣被調(diào)用(如
func()); - 狀態(tài)持有性:可通過(guò)類的成員變量存儲(chǔ)自身狀態(tài)(如計(jì)數(shù)、配置參數(shù))。
1.2 C語(yǔ)言模擬的必要性
C語(yǔ)言沒(méi)有原生“類”和“運(yùn)算符重載”,但實(shí)際開(kāi)發(fā)中常需“帶狀態(tài)的回調(diào)/函數(shù)”(如“按自定義規(guī)則排序”“帶步長(zhǎng)的計(jì)數(shù)器”)。此時(shí)需通過(guò)結(jié)構(gòu)體+函數(shù)指針模擬仿函數(shù),實(shí)現(xiàn)“可調(diào)用+持狀態(tài)”的核心能力。
1.3 C語(yǔ)言仿函數(shù)的定義
在C語(yǔ)言中,仿函數(shù)被定義為:
包含“狀態(tài)變量”和“函數(shù)指針”的結(jié)構(gòu)體——狀態(tài)變量存儲(chǔ)數(shù)據(jù),函數(shù)指針指向具體調(diào)用邏輯,調(diào)用時(shí)通過(guò)函數(shù)指針訪問(wèn)狀態(tài)并執(zhí)行邏輯。
2. 核心實(shí)現(xiàn)原理
C語(yǔ)言模擬仿函數(shù)的核心是“拆分+組合”:用結(jié)構(gòu)體管理狀態(tài),用函數(shù)指針定義行為,二者結(jié)合實(shí)現(xiàn)仿函數(shù)特性。
2.1 第一步:用結(jié)構(gòu)體存儲(chǔ)“狀態(tài)”
結(jié)構(gòu)體的成員變量用于保存仿函數(shù)需要維護(hù)的數(shù)據(jù)(如計(jì)數(shù)初始值、排序方向、配置參數(shù)等),示例:
// 示例:計(jì)數(shù)器的狀態(tài)結(jié)構(gòu)體
typedef struct {
int count; // 狀態(tài)1:當(dāng)前計(jì)數(shù)值
int step; // 狀態(tài)2:計(jì)數(shù)步長(zhǎng)(可選,擴(kuò)展用)
} CounterState;
2.2 第二步:用函數(shù)指針定義“可調(diào)用邏輯”
函數(shù)指針指向具體的“調(diào)用函數(shù)”,該函數(shù)需滿足兩個(gè)要求:
- 第一個(gè)參數(shù)必須是結(jié)構(gòu)體指針(通過(guò)指針訪問(wèn)結(jié)構(gòu)體內(nèi)部狀態(tài));
- 返回值和剩余參數(shù)根據(jù)業(yè)務(wù)場(chǎng)景定義(如計(jì)數(shù)器返回當(dāng)前值,比較器返回比較結(jié)果)。
示例函數(shù)指針定義:
// 計(jì)數(shù)器的調(diào)用函數(shù)指針:接收結(jié)構(gòu)體指針,返回當(dāng)前計(jì)數(shù)值 typedef int (*CounterCallFunc)(struct Counter* self);
2.3 第三步:組合“狀態(tài)+邏輯”為仿函數(shù)結(jié)構(gòu)體
將“狀態(tài)結(jié)構(gòu)體”與“函數(shù)指針”封裝為一個(gè)新結(jié)構(gòu)體,形成完整的仿函數(shù)類型:
// 完整的計(jì)數(shù)器仿函數(shù)結(jié)構(gòu)體
typedef struct Counter {
// 狀態(tài)部分:復(fù)用上面的狀態(tài)結(jié)構(gòu)體(或直接定義成員)
int count;
int step;
// 邏輯部分:函數(shù)指針(指向具體調(diào)用邏輯)
CounterCallFunc call;
} Counter;
2.4 第四步:初始化仿函數(shù)
提供初始化函數(shù),完成“狀態(tài)賦值”和“函數(shù)指針綁定”,確保仿函數(shù)創(chuàng)建后可直接使用:
// 初始化計(jì)數(shù)器仿函數(shù):參數(shù)為初始值和步長(zhǎng)
Counter Counter_Create(int initial, int step) {
Counter cnt;
// 初始化狀態(tài)
cnt.count = initial;
cnt.step = step;
// 綁定調(diào)用邏輯(關(guān)聯(lián)具體的函數(shù))
cnt.call = Counter_Increment;
return cnt;
}
3. 實(shí)戰(zhàn)示例
以下兩個(gè)示例覆蓋“基礎(chǔ)狀態(tài)計(jì)數(shù)”和“帶狀態(tài)回調(diào)”兩大核心場(chǎng)景,代碼可直接編譯運(yùn)行。
3.1 示例1:帶步長(zhǎng)的計(jì)數(shù)器仿函數(shù)
功能需求
- 初始化時(shí)設(shè)置“初始值”和“步長(zhǎng)”;
- 每次調(diào)用仿函數(shù),按步長(zhǎng)自增并返回當(dāng)前值;
- 支持動(dòng)態(tài)修改步長(zhǎng)(修改狀態(tài))。
完整代碼
#include <stdio.h>
// 1. 定義計(jì)數(shù)器仿函數(shù)結(jié)構(gòu)體(狀態(tài)+邏輯)
typedef struct Counter {
// 狀態(tài):計(jì)數(shù)值、步長(zhǎng)
int count;
int step;
// 邏輯:調(diào)用函數(shù)指針(接收自身指針,返回當(dāng)前值)
int (*call)(struct Counter* self);
} Counter;
// 2. 實(shí)現(xiàn)調(diào)用邏輯:按步長(zhǎng)自增并返回當(dāng)前值
int Counter_Increment(Counter* self) {
// 通過(guò)結(jié)構(gòu)體指針訪問(wèn)內(nèi)部狀態(tài)
self->count += self->step;
return self->count;
}
// 3. 初始化函數(shù):創(chuàng)建仿函數(shù)實(shí)例
Counter Counter_Create(int initial, int step) {
Counter cnt;
cnt.count = initial;
cnt.step = step;
cnt.call = Counter_Increment; // 綁定邏輯
return cnt;
}
// 4. 輔助函數(shù):動(dòng)態(tài)修改步長(zhǎng)(修改狀態(tài))
void Counter_SetStep(Counter* self, int new_step) {
self->step = new_step;
}
// 主函數(shù)測(cè)試
int main() {
// 創(chuàng)建仿函數(shù)實(shí)例:初始值0,步長(zhǎng)1
Counter cnt = Counter_Create(0, 1);
// 調(diào)用仿函數(shù)(像函數(shù)一樣使用)
printf("第1次調(diào)用:%d\n", cnt.call(&cnt)); // 輸出:1
printf("第2次調(diào)用:%d\n", cnt.call(&cnt)); // 輸出:2
// 修改狀態(tài)(步長(zhǎng)改為3)
Counter_SetStep(&cnt, 3);
printf("修改步長(zhǎng)為3后,第3次調(diào)用:%d\n", cnt.call(&cnt)); // 輸出:5
printf("第4次調(diào)用:%d\n", cnt.call(&cnt)); // 輸出:8
return 0;
}
編譯與運(yùn)行
- 編譯命令:
gcc counter_functor.c -o counter_functor - 運(yùn)行結(jié)果:
第1次調(diào)用:1 第2次調(diào)用:2 修改步長(zhǎng)為3后,第3次調(diào)用:5 第4次調(diào)用:8
3.2 示例2:帶排序方向的比較器仿函數(shù)
功能需求
- 實(shí)現(xiàn)自定義排序:支持“升序”和“降序”切換;
- 排序邏輯通過(guò)仿函數(shù)封裝(狀態(tài):排序方向;邏輯:比較規(guī)則);
- 復(fù)用排序函數(shù),僅需修改仿函數(shù)狀態(tài)即可切換排序方向。
完整代碼
#include <stdio.h>
#include <stdlib.h>
// 1. 定義比較器仿函數(shù)結(jié)構(gòu)體(狀態(tài)+邏輯)
typedef struct Comparator {
// 狀態(tài):排序方向(1=升序,0=降序)
int is_ascending;
// 邏輯:比較函數(shù)指針(接收自身、兩個(gè)待比較值,返回比較結(jié)果)
int (*compare)(struct Comparator* self, int a, int b);
} Comparator;
// 2. 實(shí)現(xiàn)比較邏輯:根據(jù)狀態(tài)返回比較結(jié)果
int Int_Compare(Comparator* self, int a, int b) {
if (self->is_ascending) {
return a - b; // 升序:a>b返回正數(shù),觸發(fā)交換
} else {
return b - a; // 降序:b>a返回正數(shù),觸發(fā)交換
}
}
// 3. 初始化比較器仿函數(shù)
Comparator Comparator_Create(int is_ascending) {
Comparator cmp;
cmp.is_ascending = is_ascending;
cmp.compare = Int_Compare;
return cmp;
}
// 4. 通用排序函數(shù)(接收仿函數(shù)作為參數(shù),復(fù)用邏輯)
void Sort_With_Functor(int* arr, int len, Comparator* cmp) {
for (int i = 0; i < len - 1; i++) {
for (int j = 0; j < len - i - 1; j++) {
// 調(diào)用仿函數(shù)的比較邏輯
if (cmp->compare(cmp, arr[j], arr[j+1]) > 0) {
// 交換元素
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
// 輔助函數(shù):打印數(shù)組
void Print_Array(int* arr, int len) {
for (int i = 0; i < len; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
// 主函數(shù)測(cè)試
int main() {
int arr[] = {5, 2, 9, 1, 5, 6};
int len = sizeof(arr) / sizeof(arr[0]);
printf("原始數(shù)組:");
Print_Array(arr, len); // 輸出:5 2 9 1 5 6
// 1. 升序排序(使用升序比較器)
Comparator asc_cmp = Comparator_Create(1);
Sort_With_Functor(arr, len, &asc_cmp);
printf("升序排序后:");
Print_Array(arr, len); // 輸出:1 2 5 5 6 9
// 2. 降序排序(復(fù)用排序函數(shù),僅修改比較器狀態(tài))
Comparator desc_cmp = Comparator_Create(0);
Sort_With_Functor(arr, len, &desc_cmp);
printf("降序排序后:");
Print_Array(arr, len); // 輸出:9 6 5 5 2 1
return 0;
}
編譯與運(yùn)行
- 編譯命令:
gcc comparator_functor.c -o comparator_functor - 運(yùn)行結(jié)果:
原始數(shù)組:5 2 9 1 5 6 升序排序后:1 2 5 5 6 9 降序排序后:9 6 5 5 2 1
4. 特點(diǎn)與局限
4.1 優(yōu)點(diǎn)
- 靈活性高:相比單純的函數(shù)指針,可攜帶狀態(tài),支持動(dòng)態(tài)調(diào)整邏輯(如示例2切換排序方向);
- 復(fù)用性強(qiáng):核心邏輯(如排序函數(shù))可復(fù)用,僅需替換仿函數(shù)實(shí)例即可改變行為;
- 貼近C語(yǔ)言特性:基于結(jié)構(gòu)體和函數(shù)指針實(shí)現(xiàn),無(wú)額外依賴,兼容性好。
4.2 局限
- 無(wú)類型安全:需手動(dòng)保證“函數(shù)指針參數(shù)”與“結(jié)構(gòu)體類型”匹配,編譯器不報(bào)錯(cuò)(如將
Counter*傳給Comparator*會(huì)導(dǎo)致運(yùn)行時(shí)錯(cuò)誤); - 語(yǔ)法繁瑣:調(diào)用時(shí)需顯式傳入結(jié)構(gòu)體指針(如
cnt.call(&cnt)),不如C++仿函數(shù)簡(jiǎn)潔; - 泛化能力弱:不支持C++模板的泛型,需為不同類型(如
int/float)單獨(dú)定義仿函數(shù)結(jié)構(gòu)體(如IntComparator/FloatComparator)。
5. 常見(jiàn)問(wèn)題(FAQ)
Q1:為什么調(diào)用仿函數(shù)時(shí)必須傳結(jié)構(gòu)體指針(如cnt.call(&cnt))?
A:因?yàn)楹瘮?shù)指針需要通過(guò)指針訪問(wèn)結(jié)構(gòu)體內(nèi)部的狀態(tài)變量(如count/is_ascending)。若傳值(cnt.call(cnt)),會(huì)創(chuàng)建結(jié)構(gòu)體副本,修改的是副本狀態(tài),原實(shí)例狀態(tài)不變。
Q2:能否將仿函數(shù)結(jié)構(gòu)體定義為指針類型(如Counter*)?
A:可以。若需動(dòng)態(tài)分配內(nèi)存(如在堆上創(chuàng)建仿函數(shù)),可將初始化函數(shù)改為返回指針:
// 動(dòng)態(tài)創(chuàng)建計(jì)數(shù)器仿函數(shù)(堆內(nèi)存)
Counter* Counter_Create_Dynamic(int initial, int step) {
Counter* cnt = (Counter*)malloc(sizeof(Counter));
if (cnt != NULL) {
cnt->count = initial;
cnt->step = step;
cnt->call = Counter_Increment;
}
return cnt;
}
注意:動(dòng)態(tài)創(chuàng)建后需手動(dòng)調(diào)用free(cnt)釋放內(nèi)存,避免內(nèi)存泄漏。
Q3:函數(shù)指針的語(yǔ)法太復(fù)雜,有沒(méi)有簡(jiǎn)化方式?
A:可通過(guò)typedef為函數(shù)指針定義別名,簡(jiǎn)化代碼。如示例2中:
// 簡(jiǎn)化前:直接在結(jié)構(gòu)體中定義函數(shù)指針
typedef struct Comparator {
int is_ascending;
int (*compare)(struct Comparator* self, int a, int b);
} Comparator;
// 簡(jiǎn)化后:先typedef函數(shù)指針,再在結(jié)構(gòu)體中使用別名
typedef int (*CompareFunc)(struct Comparator* self, int a, int b);
typedef struct Comparator {
int is_ascending;
CompareFunc compare; // 更簡(jiǎn)潔
} Comparator;
6. 擴(kuò)展場(chǎng)景
6.1 帶多狀態(tài)的仿函數(shù)
示例:實(shí)現(xiàn)“帶范圍限制的計(jì)數(shù)器”,狀態(tài)包括count(當(dāng)前值)、min(最小值)、max(最大值),調(diào)用時(shí)若超出范圍則觸發(fā)邊界處理:
typedef struct RangeCounter {
int count;
int min;
int max;
int (*call)(struct RangeCounter* self); // 調(diào)用時(shí)自增,超出范圍返回-1
} RangeCounter;
int RangeCounter_Increment(RangeCounter* self) {
if (self->count >= self->max) {
printf("已達(dá)最大值!\n");
return -1;
}
self->count++;
return self->count;
}
// 初始化:初始值0,范圍[0,5]
RangeCounter RangeCounter_Create(int min, int max) {
RangeCounter cnt;
cnt.count = min;
cnt.min = min;
cnt.max = max;
cnt.call = RangeCounter_Increment;
return cnt;
}
6.2 仿函數(shù)數(shù)組
可將多個(gè)仿函數(shù)實(shí)例存入數(shù)組,實(shí)現(xiàn)“批量調(diào)用”。如多個(gè)不同步長(zhǎng)的計(jì)數(shù)器:
int main() {
// 創(chuàng)建3個(gè)不同步長(zhǎng)的計(jì)數(shù)器
Counter counters[3] = {
Counter_Create(0, 1),
Counter_Create(0, 2),
Counter_Create(0, 3)
};
// 批量調(diào)用仿函數(shù)
for (int i = 0; i < 3; i++) {
printf("計(jì)數(shù)器%d第1次調(diào)用:%d\n", i+1, counters[i].call(&counters[i]));
}
// 輸出:
// 計(jì)數(shù)器1第1次調(diào)用:1
// 計(jì)數(shù)器2第1次調(diào)用:2
// 計(jì)數(shù)器3第1次調(diào)用:3
return 0;
}
到此這篇關(guān)于C語(yǔ)言仿函數(shù)(Functor)實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)C語(yǔ)言仿函數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
深入解析Radix Sort基數(shù)排序算法思想及C語(yǔ)言實(shí)現(xiàn)示例
基數(shù)排序和桶排序、計(jì)數(shù)排序共同是三種最常用的線性排序算法,這里我們就來(lái)深入解析Radix Sort基數(shù)排序算法思想及C語(yǔ)言實(shí)現(xiàn)示例,需要的朋友可以參考下2016-07-07
C語(yǔ)言 簡(jiǎn)單粗暴的笨方法找水仙花數(shù)
這篇文章介紹了C語(yǔ)言找水仙花數(shù)最原始的笨方法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-02-02
C++實(shí)現(xiàn)動(dòng)態(tài)數(shù)組功能
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)動(dòng)態(tài)數(shù)組功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-11-11
C語(yǔ)言文字藝術(shù)之?dāng)?shù)據(jù)輸入輸出
這篇文章主要介紹了C語(yǔ)言文字藝術(shù)之?dāng)?shù)據(jù)輸入輸出,C語(yǔ)言的語(yǔ)句用來(lái)向計(jì)算機(jī)系統(tǒng)發(fā)出操作指令。一條語(yǔ)句編寫(xiě)完成經(jīng)過(guò)編譯后產(chǎn)生若干條機(jī)器指2022-07-07
C++中的移動(dòng)構(gòu)造函數(shù)及move語(yǔ)句示例詳解
這篇文章主要給大家介紹了關(guān)于C++中移動(dòng)構(gòu)造函數(shù)及move語(yǔ)句的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用C++具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-10-10
Qt GUI圖形圖像開(kāi)發(fā)之QT表格控件QTableView詳細(xì)使用方法與實(shí)例
這篇文章主要介紹了Qt GUI圖形圖像開(kāi)發(fā)之QT表格控件QTableView詳細(xì)使用方法與實(shí)例,需要的朋友可以參考下2020-03-03

