原文地址为:
(原創) Function Pointer、Delegate和Function Object (C/C++) (template) (.NET) (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夢在天涯的函数指针实例
转载请注明本文地址:
(原創) Function Pointer、Delegate和Function Object (C/C++) (template) (.NET) (C#)