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,則寫法如下
2
3using namespace std;
4
5void printArrayOdd(int* beg, int* end) {
6 while(beg != end) {
7 if ((*beg)%2)
8 cout << *beg << endl;
9
10 beg++;
11 }
12}
13
14void printArrayEven(int* beg, int* end) {
15 while(beg != end) {
16 if (!((*beg)%2))
17 cout << *beg << endl;
18
19 beg++;
20 }
21}
22
23void printArrayGreaterThan2(int* beg, int* end) {
24 while(beg != end) {
25 if ((*beg)>2)
26 cout << *beg << endl;
27
28 beg++;
29 }
30}
31
32int 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}
執行結果
1
3
Even
2
Greater than 2
3
以功能而言沒有問題,但每個function都要做迴圈與判斷,似乎重覆了,而且將來若有新的判斷,又要copy整個迴圈,然後改掉判斷式,若能將迴圈與判斷式分離,若日後有新的判斷式,只要將該判斷式傳進來即可,這就是function pointer概念。
使用C語言的Function Pointer
2(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4Filename : FuntionPointer.cpp
5Compiler : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
6Description : Demo how to use function pointer
7Release : 05/01/2007 1.0
8*/
9#include <iostream>
10
11using namespace std;
12
13typedef bool (*predicate)(int);
14
15bool isOdd(int i) {
16 return i%2? true : false;
17}
18
19bool isEven(int i) {
20 return i%2? false : true;
21}
22
23bool greaterThan2(int i) {
24 return i > 2;
25}
26
27void 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
36int 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}
執行結果
1
3
Even
2
Greater than 2
3
第13行
宣告了predicate這個function ponter型別,指向回傳值為bool,參數為int的function,值得注意的是(*predicate)一定要括號刮起來,否則compiler會以為是bool*,我承認這個語法很奇怪,但若仔細想想,若我是C語言發明者,我應該也是這樣定語法,因為也沒其他更好的語法了:D。
這個範例將判斷式和迴圈分開,日後若有新的判斷式,只要新增判斷式即可,funtion pointer提供了一個型別,讓參數可以宣告function pointer型別
如此我們就可以將function傳進另外一個fuction了。
使用C#的Delegate
C#是個物件導向的語言,為了提供類似function pointer的機制,提出了delegate概念,delegate英文是『委託、代表』,表示可以代表一個function,可以將delegate想成物件導向的function pointer。
2(C) OOMusou 2007 http://oomusou.cnblogs.com
3
4Filename : Delegate.cs
5Compiler : Visual Studio 2005 / C# 2.0
6Description : Demo how to use delegate
7Release : 05/01/2007 1.0
8*/
9
10using System;
11
12class 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}
執行結果
1
3
Even
2
Greater than 2
3
整個C#程式和C語言程式幾乎是一對一對應,定義function pointer型別變成了13行