假定我們被要求提供一個如下形式的排序函數 :
sort( start, end, compare );
start 和 end是指向字串數組中元素的指標,函數 sort()對於 start 和end之間的數組元素進行排序,compare 定義了比較數組中兩個字串的比較操作(提供一個比較的策略【比較函數 】)。
為簡化 sort()的用法而又不限制它的靈活性,我們可能希望指定一個預設的比較函數。
如何來實現呢?compare可能指向不同的排序方法,解決這些需求的一種策略是將第三個參數compare 設為函數指標,並由它指定要使用的比較函數 。
一、指向函數的指標的類型
怎樣聲明指向函數的指標呢 用函數指標作為實參的參數會是什麼樣呢,下面是函數lexicoCompare()的定義,它按字典序比較兩個字串
#include <string>
int lexicoCompare( const string &s1, const string &s2 ) {
return s1.compare(s2);
}
函數名不是其類型的一部分,函數的類型只由它的傳回值和參數表決定,指向lexicoCompare()的指標必須指向與lexicoCompare()相同類型的函數:帶有相同的傳回型別和相同的參數表,讓我們試一下:
int *pf( const string &, const string & ); // 喔! 差一點
這幾乎是正確的,問題是編譯器把該語句解釋成名為pf的函數的聲明,它有兩個參數並且返回一個int*型的指標,參數表是正確的,但是傳回值不是我們所希望的解引用操作符(*)應與傳回型別關聯,所以在這種情況下是與類型名 int 關聯,而不是 pf ,要想讓解 引用操作符與pf關聯,括弧是必需的:
int (*pf)( const string &, const string & ); // ok: 正確
這個語句聲明了pf是一個指向函數的指標,該函數有兩個參數和 int 型的傳回值,即指向函數的指標,它與 lexicoCompare()的類型相同,下列函數與lexicoCompare()類型相同,都可以用 pf來指向:
int sizeCompare( const string &, const string & );
但是 calc()和gcd()與前面兩個函數的類型不同 不能用 Pf來指
int calc( int , int );
int gcd( int , int );
可以如下定義 pfi 它能夠指向這兩個函數
int (*pfi)( int, int );
省略符號是函數類型的一部分,如果兩個函數具有相同的參數表,但是一個函數在參數表末尾有省略符號,則它們被視為不同的函數類型,指向這兩個函數的指標類型也不同
int printf( const char*, ... );
int strlen( const char* );
int (*pfce)( const char*, ... ); // 可以指向 printf()
int (*pfc)( const char* ); // 可以指向 strlen()
函數傳回型別和參數表的不同組合 代表了各不相同的函數類型
二、初始化和賦值
我們知道,不帶下標操作符的數組名會被解釋成指向首元素的指標,當一個函數名沒被叫用作業符修飾時,會被解釋成指向該類型函數的指標,例如,運算式:
lexicoCompare;
被解釋成類型
int (*)( const string &, const string & );
的指標
將取地址操作符作用在函數名上也能產生指向該函數類型的指標,因此 lexicoComp和&lexioCompare類型相同,指向函數的指標可如下被初始化 :
int (*pfi)( const string &, const string & ) = lexicoCompare;
int (*pfi2)( const string &, const string & ) = &lexicoCompare;
指向函數的指標可以如下被賦值 :
pfi = lexicoCompare;
pfi2 = pfi;
只有當賦值操作符左邊指標的參數表和傳回型別與右邊函數或指標的參數表和返回類完全符合時,初始化和賦值才是正確的,如果不匹配,則將產生編譯錯誤訊息,在指向函類型的指標之間不存在隱式類型轉換,例如 :
int calc( int, int );
int (*pfi2s)( const string &, const string & ) = 0;
int (*pfi2i)( int, int ) = 0;
int main() {
pfi2i = calc; // ok
pfi2s = calc; // 錯誤: 類型不符
pfi2s = pfi2i; // 錯誤: 類型不符
return 0;
}
函數指標可以用 0 來初始化或賦值 以表示該指標不指向任何函數
三、調用
指向函數的指標可以被用來調用它所指向的函數,調用函數時,不需要解引用操作符,無論是用函數名直接調用函數,還是用指標間接調用函數,兩者的寫法是一樣的,例如 :
#include <iostream>
int min( int*, int );
int (*pf)( int*, int ) = min;
const int iaSize = 5;
int ia[ iaSize ] = { 7, 4, 9, 2, 5 };
int main() {
cout << "Direct call: min: "
<< min( ia, iaSize ) << endl;
cout << "Indirect call: min: "
<< pf( ia, iaSize ) << endl;
return 0;
}
int min( int* ia, int sz ) {
int minVal = ia[ 0 ];
for ( int ix = 1; ix < sz; ++ix )
if ( minVal > ia[ ix ] )
minVal = ia[ ix ];
return minVal;
}
調用
pf( ia, iaSize );
也可以用顯式的指標符號寫出
(*pf)( ia, iaSize );
這兩種形式產生相同的結果,但是第二種形式讓讀者更清楚該調用是通過函數指標執行的 。
當然,如果函數指標的值為 0,則兩個調用都將導致運行時刻錯誤,只有已經被初始化或賦值的指標引用到一個函數,才可以被安全地用來調用一個函數。