zoukankan      html  css  js  c++  java
  • (原創) Function Pointer、Delegate和Function Object (C/C++) (template) (.NET) (C#)

    From:真 OO无双 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[] = {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
     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}


    執行結果

    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 = {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
     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[] = {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的應用。

  • 相关阅读:
    Java中数据类型的分类
    PL/SQL Developer工具
    Oracle数据库SQL语句的分类
    Oracle数据库控制台常用命令
    关于C#中泛型类型参数约束(where T : class)
    C#动态操作DataTable(新增行、列、查询行、列等)
    ADO.NET 全面整理
    区块链入门教程
    排序算法汇总
    常用SQL
  • 原文地址:https://www.cnblogs.com/xiangshancuizhu/p/2857997.html
Copyright © 2011-2022 走看看