Function Pointer(C)、Delegate(C#)和Function Object(C++)這三個其實是一樣的功能,所以在此一併討論。
Abstract
Function Pointer(C)、Delegate(C#)和Function Object(C++)這三個其實是一樣的功能,所以在此一併討論。
Introduction
function pointer是C語言中最高級的機制,大概很多人還沒上到這裡已經學期末了,所以不少C語言工程師根本不知道C語言有function pointer;而C#的delegate大抵跟C語言的function pointer功能相同,所以很多書說delegate是物件導向的function pointer;C++的function object功能則比function pointer略強,還可配合泛型使用。
為什麼會需要function pointer、delegate、function object這種機制呢?源於一個很簡單的想法:『為什麼我們不能將function也如同變數一樣傳進另外一個function呢?』,C語言的解決方式是,利用pointer指向該function,將該pointer傳入另外一個function,只要將該pointer dereference後,就如同存取原function一樣。C#解決的方式是,將function包成delegate object,傳入另外一個function。C++的解決方式是,利用class或struct將function包成object,傳入另外一個function。
一個很簡單的需求,想個別列出陣列中,所有奇數、偶數、和大於2的數字,若使用傳統方式,而不使用function pointer,則寫法如下
1
#include <iostream>
2
3
using namespace std;
4
5
void printArrayOdd(int* beg, int* end)
{
6
while(beg != end)
{
7
if ((*beg)%2)
8
cout << *beg << endl;
9
10
beg++;
11
}
12
}
13
14
void printArrayEven(int* beg, int* end)
{
15
while(beg != end)
{
16
if (!((*beg)%2))
17
cout << *beg << endl;
18
19
beg++;
20
}
21
}
22
23
void printArrayGreaterThan2(int* beg, int* end)
{
24
while(beg != end)
{
25
if ((*beg)>2)
26
cout << *beg << endl;
27
28
beg++;
29
}
30
}
31
32
int main()
{
33
int ia[] =
{1, 2, 3};
34
35
cout << "Odd
" << endl;
36
printArrayOdd(ia, ia + 3);
37
38
39
cout << "Even
" << endl;
40
printArrayEven(ia, ia + 3);
41
42
cout << "Greater than 2
" << endl;
43
printArrayGreaterThan2(ia, ia + 3);
44
}
執行結果
Odd
1
3
Even
2
Greater than 2
3
以功能而言沒有問題,但每個function都要做迴圈與判斷,似乎重覆了,而且將來若有新的判斷,又要copy整個迴圈,然後改掉判斷式,若能將迴圈與判斷式分離,若日後有新的判斷式,只要將該判斷式傳進來即可,這就是function pointer概念。
使用C語言的Function Pointer
1
/**//*
2
(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4
Filename : FuntionPointer.cpp
5
Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
6
Description : Demo how to use function pointer
7
Release : 05/01/2007 1.0
8
*/
9
#include <iostream>
10
11
using namespace std;
12
13
typedef bool (*predicate)(int);
14
15
bool isOdd(int i)
{
16
return i%2? true : false;
17
}
18
19
bool isEven(int i)
{
20
return i%2? false : true;
21
}
22
23
bool greaterThan2(int i)
{
24
return i > 2;
25
}
26
27
void printArray(int* beg, int* end, predicate fn)
{
28
while(beg != end)
{
29
if ((*fn)(*beg))
30
cout << *beg << endl;
31
32
beg++;
33
}
34
}
35
36
int main()
{
37
int ia[] =
{1, 2, 3};
38
39
cout << "Odd
" << endl;
40
printArray(ia, ia + 3, isOdd);
41
42
cout << "Even
" << endl;
43
printArray(ia, ia + 3, isEven);
44
45
cout << "Greater than 2
" << endl;
46
printArray(ia, ia + 3, greaterThan2);
47
}
執行結果
Odd
1
3
Even
2
Greater than 2
3
第13行
typedef bool (*predicate)(int);
宣告了predicate這個function ponter型別,指向回傳值為bool,參數為int的function,值得注意的是(*predicate)一定要括號刮起來,否則compiler會以為是bool*,我承認這個語法很奇怪,但若仔細想想,若我是C語言發明者,我應該也是這樣定語法,因為也沒其他更好的語法了:D。
這個範例將判斷式和迴圈分開,日後若有新的判斷式,只要新增判斷式即可,funtion pointer提供了一個型別,讓參數可以宣告function pointer型別

void printArray(int* beg, int* end, predicate fn)
{
如此我們就可以將function傳進另外一個fuction了。
使用C#的Delegate
C#是個物件導向的語言,為了提供類似function pointer的機制,提出了delegate概念,delegate英文是『委託、代表』,表示可以代表一個function,可以將delegate想成物件導向的function pointer。
1
/**//*
2
(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4
Filename : Delegate.cs
5
Compiler : Visual Studio 2005 / C# 2.0
6
Description : Demo how to use delegate
7
Release : 05/01/2007 1.0
8
*/
9
10
using System;
11
12
class main
{
13
public delegate bool predicate(int i);
14
15
public static bool isOdd(int i)
{
16
return (i % 2) > 0? true : false;
17
}
18
19
public static bool isEven(int i)
{
20
return ((i % 2) > 0? false : true);
21
}
22
23
public static bool greaterThan2(int i)
{
24
return i > 2;
25
}
26
27
public static void printArray(int[] arr, int size, predicate fn)
{
28
for(int i = 0; i != size; ++i)
{
29
if (fn(arr[i]))
30
Console.WriteLine(arr[i].ToString());
31
}
32
}
33
34
public static void Main()
{
35
int[] ia =
{1, 2, 3};
36
37
Console.WriteLine("Odd
");
38
printArray(ia, 3, new predicate(isOdd));
39
40
Console.WriteLine("Even
");
41
printArray(ia, 3, new predicate(isEven));
42
43
Console.WriteLine("Greater than 2
");
44
printArray(ia, 3, new predicate(greaterThan2));
45
}
46
}
執行結果
Odd
1
3
Even
2
Greater than 2
3
整個C#程式和C語言程式幾乎是一對一對應,定義function pointer型別變成了13行
public delegate bool predicate(int i);
表示predicate是一個delegate型別,代表一個迴傳為bool,參數為int的function。
而原來宣告function pointer型態的參數,則改成delegate型態

public static void printArray(int[] arr, int size, predicate fn)
{
使用C++的Function Object
function object也稱為functor,用class或struct都可以,因為function object是利用constructor和對operator()做overloading,而這些都是public的,所以大部分人就直接使用struct,可少打public:這幾個字。
1
/**//*
2
(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4
Filename : FuntionObject.cpp
5
Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
6
Description : Demo how to use function object
7
Release : 05/01/2007 1.0
8
*/
9
#include <iostream>
10
11
using namespace std;
12
13
template <typename T>
14
struct isOdd
{
15
bool operator() (T i)
{
16
return i%2? true : false;
17
}
18
};
19
20
template <typename T>
21
struct isEven
{
22
bool operator() (T i)
{
23
return i%2? false : true;
24
}
25
};
26
27
template <typename T>
28
struct greaterThan2
{
29
bool operator() (T i)
{
30
return i > 2;
31
}
32
};
33
34
template <typename T>
35
struct greaterThanAny
{
36
T _val;
37
greaterThanAny(T n) : _val(n)
{}
38
bool operator() (T i)
{
39
return i > _val;
40
}
41
};
42
43
44
template <typename T, typename U>
45
void printArray(T beg, T end, U fn)
{
46
while(beg != end)
{
47
if (fn(*beg))
48
cout << *beg << endl;
49
50
beg++;
51
}
52
};
53
54
int main()
{
55
int ia[] =
{1, 2, 3};
56
57
cout << "Odd
" << endl;
58
printArray(ia, ia + 3, isOdd<int>());
59
60
cout << "Even
" << endl;
61
printArray(ia, ia + 3, isEven<int>());
62
63
cout << "Greater than 2
" << endl;
64
printArray(ia, ia + 3, greaterThan2<int>());
65
66
cout << "Greater than any
" << endl;
67
printArray(ia, ia + 3, greaterThanAny<int>(1));
68
}
執行結果
Odd
1
3
Even
2
Greater than 2
3
Greater than any
2
3
13行
template <typename T>

struct isOdd
{

bool operator() (T i)
{
return i%2? true : false;
}
};
使用了template,不過並非必要,只是顯示function object可以搭配template使用,而使用的技巧只是將function內的東西搬到operator()內。
34行
template <typename T>

struct greaterThanAny
{
T _val;

greaterThanAny(T n) : _val(n)
{}

bool operator() (T i)
{
return i > _val;
}
};
是function object優於function pointer和delegate之處,由C語言和C#的範例可知,我們只能寫一個greaterThan2()的判斷式,若今天需求改變成greaterThan3,則又得再寫一個判斷式了,但因為function object是透過struct和class,別忘了struct和class還有個constructor,所以能藉由constructor對class做初始化,因此才能寫出greaterThanAny(),大於多少只要當成constructor帶入即可,而operator()的寫法一樣不變。
Conclusion
C語言、C#、C++皆提供了『將函數傳到另外一個函數』的機制,function pointer和delegate相當類似,而funtion object則功能更強。這裡澄清一個觀念,很多人認為function object就是為了要使用STL的algorithm才使用,這是標準的錯誤觀念,這是果而非因,因為STL的algorithm使用了function object的方式,所以我們才去配合,並不是只用在這個地方,事實上,我們自己的也可以使用function object,而且其比function pointer優越之處就在於function object多了constructor,所以比function pointer彈性更大。
實務上會用在哪些地方呢?大概有三個地方,callback,multi-thread和event,我會另外開一個專文專講function object的應用。
See Also
(原創) 如何使用Function Object? (C/C++) (STL)
(原創) 如何使用for_each() algorithm? (C/C++) (STL)
(原創) 如何正確的使用迴圈(使用for_each)? (C/C++) (STL) (template)
(原創) 如何為程式碼加上行號? (C/C++) (STL)
(原創) Function Pointer、Delegate與Function Object (C/C++) (template) (.NET) (C#)
Reference
子由,深度學習C++ 2/e,博碩文化,2006
夢在天涯的函数指针实例
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述