close
Function Pointer and Function Object

Function Pointer
函式指標的宣告方式如下所示:
傳回值型態 (*指標名稱) (傳遞參數)
ex: int (*func)(int, float, string); or int (*func)(int, int, int); ...

為什麼會需要 function pointer、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,則寫法如下:
#include <iostream>
using namespace std;
void printArrayOdd(int* beg, int* end) {
    while(beg != end) {
        if ((*beg)%2)
            cout << *beg << endl;
        beg++;
    }
}
void printArrayEven(int* beg, int* end) {
    while(beg != end) {
        if (!((*beg)%2))
            cout << *beg << endl;
        beg++;
    }
}
void printArrayGreaterThan2(int* beg, int* end) {
    while(beg != end) {
        if ((*beg)>2)
            cout << *beg << endl;
        beg++;
    }
}

int main() {
    int ia[] = {1, 2, 3};
    cout << "Odd" << endl;
    printArrayOdd(ia, ia + 3);
    cout << "Even" << endl;
    printArrayEven(ia, ia + 3);
    cout << "Greater than 2" << endl;
    printArrayGreaterThan2(ia, ia + 3);
}

以功能而言沒有問題,但每個 function 都要做迴圈與判斷,似乎重覆了,而且將來若有新的判斷,又要 copy 整個迴圈,然後改掉判斷式,如果可以將迴圈與判斷式分離,日後如果再有新的判斷式,只要將該判斷式傳進來即可,這就是 function pointer 概念:
#include <iostream>
using namespace std;
typedef bool (*predicate) (int);

bool isOdd(int i) {
    return ((i%2) ? true : false);
}
bool isEven(int i) {
    return ((i%2) ? false : true);
}
bool isGreaterThan2(int i) {
    return i>2;
}
// 以 predicate fn 取代 bool (*fn)(int)
bool printArray(int *beg, int *end, predicate fn) {
    while ( beg != end ) {
        if ( (*fn)(*beg) )
            cout << *beg << "\t";
        ++beg;
    }
    cout << endl << endl;
}
int main(int argc, char* argv[])
{
    int ia[7] = {1, 2, 3, 4, 5, 6, 7};
    int size = sizeof(ia) / sizeof(int);

    cout << "Odd :" << endl;
    printArray(ia, ia+size, isOdd);

    cout << "Even :" << endl;
    printArray(ia, ia+size, isEven);

    cout << "Greater than 2 :" << endl;
    printArray(ia, ia+size, isGreaterThan2);
}

關鍵行:
typedef bool (*predicate)(int); 

宣告了 predicate 這個 function pointer 型別,指向回傳值為 bool,參數為 int 的 function,值得注意的是 (*predicate) 一定要括號刮起來,否則 compiler 會以為是 bool*,或許這個語法很奇怪,但好像也沒其他更好的語法了。
這個範例將判斷式和迴圈分開,日後若有新的判斷式,只要新增判斷式即可,function pointer 提供了一個型別,讓參數可以宣告 function pointer 型別。

至於另一種寫法是不需要 typedef 的寫法,也可參考看看:
bool printArray(int *beg, int *end, bool (*fn)(int)) {
    ...
}



Function Object
function object 也稱為 functor,用 class 或 struct 來實作都可以,不過因為 function object 是利用 constructor 及對 operator() 做 overloading,而這些都是 public 的,所以大多數人選擇直接使用 struct,這樣可以少打 public: 這幾個字:
#include <iostream>

using namespace std;

template <typename T>
struct isOdd {
    bool operator() (T i) {
        return i%2 ? true : false;
    }
};

template <typename T>
struct isEven {
    bool operator() (T i) {
        return i%2 ? false : true;
    }
};

template <typename T>
struct greaterThan2 {
    bool operator() (T i) {
        return i > 2;
    }
};

template <typename T>
struct greaterThanAny {
    T _val;
    greaterThanAny(T n) : _val(n) {}
    bool operator() (T i) {
        return i > _val;
    }
};

template <typename T, typename U>
void printArray(T beg, T end, U fn) {
    while (beg != end) {
        if (fn(*beg))
            cout << *beg << endl;
        beg++;
    }
};

int main() {
    int ia[] = {1, 2, 3};
    
    cout << "Odd" << endl;
    printArray(ia, ia + 3, isOdd<int>());
    
    cout << "Even" << endl;
    printArray(ia, ia + 3, isEven<int>());
    
    cout << "Greater than 2" << endl;
    printArray(ia, ia + 3, greaterThan2<int>());
    
    cout << "Greater than any" << endl;
    printArray(ia, ia + 3, greaterThanAny<int>(1));
}

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

而 function object 優於 function pointer 之處,由 C 語言範例可知,若今天需求改變成 greaterThan3 ,則又得再寫一個判斷式了,但因為 function object 是透過 struct 或 class,而 struct 和 class 還有個 constructor 可以運用,所以能藉由 constructor 對 class 做初始化,因此能寫出 greaterThanAny(),大於多少只要從 constructor 帶入即可,而 operator() 的寫法則不變:
template <typename T, typename U>
void printArray(T beg, T end, U fn) {
    while (beg != end) {
        if (fn(*beg))
            cout << *beg << endl;
        beg++;
    }
};


Another example about function pointer:
#include "stdio.h"

int func_1() {
  printf("> function 1.\n");
  return 1;
}

int func_2() {
  printf("> function 2.\n");
  return 2;
}

int func_3() {
  printf("> function 3.\n");
  return 3;
}

int func_4() {
  printf("> function 4.\n");
  return 4;
}

int func_5() {
  printf("> function 5.\n");
  return 5;
}

void main() {

  int (*f_ptr[5])() = { func_1, func_2, func_3, func_4, func_5 };
  for (int i = 0; i < 5; i++)
      (*f_ptr[i])();
}



Ref: Function Pointer(C)、Delegate(C#)和Function Object(C++)
arrow
arrow
    全站熱搜

    silverfoxkkk 發表在 痞客邦 留言(0) 人氣()