7/14/2010

[轉] Function Pointer、Delegate和Function Object (C/C++) (template) (.NET) (C#)

本文轉自 : http://www.cnblogs.com/oomusou/archive/2007/05/02/734290.html


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
 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[] = {123};
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
 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[] = {123};
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
 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 = {123};
36  
37    Console.WriteLine("Odd");
38    printArray(ia, 3new predicate(isOdd));
39
40    Console.WriteLine("Even");
41    printArray(ia, 3new predicate(isEven));
42
43    Console.WriteLine("Greater than 2");
44    printArray(ia, 3new 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
 4Filename    : FuntionObject.cpp
 5Compiler    : Visual C++ 8.0 / BCB 6.0 / gcc 3.4.2 / ISO C++
 6Description : Demo how to use function object
 7Release     : 05/01/2007 1.0
 8*/

 9#include <iostream>
10
11using namespace std;
12
13template <typename T>
14struct isOdd {
15  bool operator() (T i) {
16    return i%2? true : false;
17  }

18}
;
19
20template <typename T>
21struct isEven {
22  bool operator() (T i) {
23    return i%2? false : true;
24  }

25}
;
26
27template <typename T>
28struct greaterThan2 {
29  bool operator() (T i) {
30    return i > 2;
31  }

32}
;
33
34template <typename T>
35struct greaterThanAny {
36  T _val;
37  greaterThanAny(T n) : _val(n) {}
38  bool operator() (T i) {
39    return i > _val;
40  }

41}
;
42
43
44template <typename T, typename U>
45void 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
54int main() {
55  int ia[] = {123};
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的應用。