在 C++ 裡傳遞、儲存函式 Part 1:Function Pointer

| | 0 Comments| 10:45
Categories:

這一系列文章,主要是整理一下 Heresy 自己知道,可以用來儲存、傳遞一個函式(function)的一些方法;當然,主要就是 function pointer 和 function object 了∼為什麼要做這件事呢?其實如果妥善利用的,這類的功能是相當實用,不但可以減少不少程式碼的量的,同時更可以減少程式碼維護所需要的時間。

而首先,這一篇就先來大概介紹一下 C 的 funciton pointer(pointer to function)了∼

Function Pointer 基本範例

顧名思義,他就是指到 function 的指標。下面是一個簡單的範例:

int power2( int v ){  return v * v;} void custom_for_each( int *pArray, unsigned int size, int (*op)( int ) ){  for( unsigned int i = 0; i < size;    i )    pArray[i] = (*op)( pArray[i] );} int main( int argc, char* argv[] ){  int size = 10;  int* pArray = new int[ size ];  for( unsigned int i = 0; i < size;    i )    pArray[i] = i;   custom_for_each( pArray, size, &power2 );   for( unsigned int i = 0; i < size;    i )    printf( "%d, ", pArray[i] );   delete [] pArray;  return 0;}

在這個例子裡,除了 main() 以外,還有定義了兩個 function:power2()custom_for_each()

其中,power2() 相當簡單,就是傳入一個整數,然後計算他的平方後傳回來。

custom_for_each() 這個函式則就是這邊的重點,它的用途是傳入一個整數的陣列 pArray 和他的大小 size,以及一個 function pointer op;而這邊這個 function pointer 的寫法,就是「int (*op)( int )」,它代表了一個名為 op 的 pointer,指到一個「接受一個整數當作參數、並回傳一個整數的 function」。

custom_for_each() 的內容部分,則就是一個迴圈,把 pArray 裡的每一項,都經過 op 做計算,並寫回 pArray 裡;而呼叫 op 的方法,基本上就是「(*op)( pArray[i] )」,也就是像一般的指標一樣,在前面加上「*」了∼

而實際上要怎麼使用這兩個函式呢?只要在呼叫 custom_for_each() 的時候,把 power2() 的位址當作第三個參數傳進去就可以了!完整寫出來,就是「custom_for_each( pArray, size, &square );」了。而實際上,只要是「接受一個整數當作參數、並回傳一個整數的 function」,都是可以以這樣的形式當作參數傳進 custom_for_each() 使用的∼

這樣寫的好處,在於可以把 function 拆開成更小的元件,讓他們可以更方便地組合、使用,有效地增加程式碼的可重複使用性。現在範例裡只有 power2() 一個函式或許感覺不太出來,但是如果我們又去額外寫了其他他的函式的話,就比較看的出來了∼比如說,假設我們現在有下面這六個 function:

int plus_one( int v );int power2( int v );int power3( int v );int power4( int v );void custom_for_each( int *pArray, unsigned int size, int (*op)( int ) );void custom_for_each_2D( int **pArray, int sizeX, int sizeY, int (*op)( int ) );

應該就比較感覺得出來這樣寫的好處了!因為前面四個計算用的 function 可以任意傳給 custom_for_each() 或來當作 custom_for_each_2D() 參數,在這個狀況下,就是有 4 * 2、共有八種組合了∼而如果函式的內容複雜、數量在更多的話,這樣不但可以減少許多本來需要重複撰寫的程式碼,也可以有效降低程式碼維護的時間。

 

使用 typedef 加強可讀性

雖然這樣就可以使用 function pointer 了,但是說實話看起來實在很醜、很難閱讀;「int (*op)(int)」這樣的寫法,真的很難一眼就看出來哪個才是變數。

所以,如果要經常性地使用某種形式的 function pointer 的時候,其實可以考慮透過「typedef」的方式,幫這種比較複雜的 function pointer 型別,定義出一個比較簡單、一般的型別名稱來用。例如以上面的例子來說,就可以改寫成:

typedef int (*CustomOperator)( int ); //void custom_for_each( int *pArray, unsigned int size, int (*op)( int ) )  void custom_for_each( int *pArray, unsigned int size, CustomOperator op )

也就是可以先透過第一行的「typedef int (*CustomOperator)( int );」,讓 CustomOperator 變成是一個 function pointer 的型別,而之後就可以把它當作一般的型別來使用了∼像 custom_for_each() 的宣告,就可以直接用「CustomOperator op」來代替本來的「int (*op)( int )」!這樣看起來,就簡單易懂多了∼

 

Function Pointer 的陣列

而在某些情況下,我們或許會需要使用到 function pointer 的陣列,這時候該怎麼寫呢?基本的寫法,是如同下面這樣的形式:

int (*op[10])( int );for( int i = 0; i < 10;    i )  op[i] = &power2;

在這裡,「int (*op[10])( int );」就是會宣告出一個大小是 10 的 function pointer 陣列。在指定它裡面每一項的值的時候,都和本來的 function pointer 寫法一樣;不過在使用的時候,只要寫「op[1]( 10 )」這樣就可了,不用再加上星號(其實就和一般的陣列意義一樣)。

當然,這樣看起來還是很難閱讀,所以我們還是可以用 typedef 的方法,來加強程式的可讀性∼也就是可以將上面的程式碼修改為:

typedef int (*CustomOperator)( int );CustomOperator op[10];for( int i = 0; i < 10;    i )  op[i] = &power2;

這樣看起來,function pointer 就更像一般的變數、更容易閱讀、使用了!

Leave a Reply

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *