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


這一系列文章,主要是整理一下 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, &power2 );」了。而實際上,只要是「接受一個整數當作參數、並回傳一個整數的 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 就更像一般的變數、更容易閱讀、使用了!


Part 2. Function Object
Part 3. Function Object in TR1

廣告

對「在 C++ 裡傳遞、儲存函式 Part 1:Function Pointer」的想法

  1. typedef int (AFX_CDEL *CustomOperator)( int );
    謝謝您的文章,還想請問您,如果在CustomOperator這個型別名稱之前加了AFX_CDEL這東西,是什麼意思呢?

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com 標誌

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 /  變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

連結到 %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.