函數(shù)指針指向的是函數(shù)而非對(duì)象绎秒。和其他指針一樣一姿,函數(shù)指針指向某種特定類型拖云。函數(shù)的類型由它的返回類型和形參類型共同決定那伐,與函數(shù)名稱無(wú)關(guān)踏施。例如:
bool LengthCompare(const std::string&, const std::string&);
該函數(shù)的類型是 bool (const std::string&, const std::string&)。要想聲明一個(gè)可以指向該函數(shù)的指針罕邀,只需要用指針替換函數(shù)名即可:
bool (*lcPtr)(const std::string&, const std::string&);
注意:*lcPtr 兩端的括號(hào)必不可少畅形。如果不寫這對(duì)括號(hào),則 lcPtr 是一個(gè)返回值為 bool 指針的函數(shù)诉探。
使用函數(shù)指針
當(dāng)我們把函數(shù)名作為一個(gè)值使用時(shí)日熬,該函數(shù)自動(dòng)轉(zhuǎn)換為指針。例如:
lcPtr = LengthCompare; // lcPtr 指向名為 LengthCompare 的函數(shù)
lcPtr = &LengthCompare; // 等價(jià)的賦值語(yǔ)句:取地址符是可選的
此外肾胯,我們還能直接使用指向函數(shù)的指針調(diào)用該函數(shù)竖席,無(wú)須提前解引用指針:
bool b1 = lcPtr("Hello", "World!"); // 調(diào)用 LengthCompare 函數(shù)
bool b2 = (*lcPtr)("Hello", "World!"); // 一個(gè)等價(jià)的調(diào)用
bool b3 = LengthCompare("Hello", "World!"); // 另一個(gè)等價(jià)的調(diào)用
在指向不同函數(shù)類型的指針間不存在轉(zhuǎn)換規(guī)則。但是和往常一樣敬肚,我們可以為函數(shù)指針賦值一個(gè) nullptr毕荐,表示該指針沒(méi)有指向任何一個(gè)函數(shù):
bool LengthCompare(const std::string&, const std::string&);
bool (*lcPtr)(const std::string&, const std::string&);
std::string::size_type sumLength(const std::string&, const std::string&);
bool cstringCompare(const char*, const char*);
lcPtr = nullptr; // 正確:形參類型不匹配
lcPtr = sumLength; // 錯(cuò)誤:返回類型不匹配
lcPtr = cstringCompare; // 錯(cuò)誤:形參類型不匹配
lcPtr = LengthCompare; // 正確:函數(shù)和指針的類型精確匹配
重載函數(shù)的指針
當(dāng)我們使用重載函數(shù)時(shí),代碼必須清晰地指明到底應(yīng)該使用哪個(gè)函數(shù)艳馒。例如:
void ff(int*);
void ff(unsigned);
編譯器通過(guò)指針類型決定選用哪個(gè)函數(shù)憎亚,指針類型必須與重載函數(shù)中的某一個(gè)精確匹配:
void ff(int*);
void ff(unsigned);
void (*pf1)(unsigned) = ff; // pf1 指向 ff(unsigned)
void (*pf2)(int) = ff; // 錯(cuò)誤
void (*pf3)(int*) = ff; // 錯(cuò)誤
函數(shù)指針形參
和數(shù)組類似,雖然不能定義函數(shù)類型的形參弄慰,但是形參可以是指向函數(shù)的指針第美。此時(shí),形參看起來(lái)是函數(shù)類型陆爽,實(shí)際上卻是當(dāng)成指針使用:
// 第三個(gè)參數(shù)是函數(shù)類型什往,它會(huì)自動(dòng)地轉(zhuǎn)換成指向函數(shù)的指針
void useBigger(const std::string &s1, const std::string &s2, bool pf(const std::string&, const std::string&));
// 等價(jià)的聲明:顯示地將形參定義成指向函數(shù)的指針
void useBigger(const std::string &s1, const std::string &s2, bool (*pf)(const std::string&, const std::string&));
我們可以直接把函數(shù)作為實(shí)參使用,此時(shí)它會(huì)自動(dòng)轉(zhuǎn)換成指針:
useBigger(s1, s2, LengthCompare);
useBigger 的聲明語(yǔ)句看起來(lái)冗長(zhǎng)而繁瑣慌闭。類型別名和 decltype 能讓我們簡(jiǎn)化使用了函數(shù)指針的代碼:
// Func1 和 Func2 是函數(shù)類型
typedef bool Func1(const std::string&, const std::string&);
typedef decltype (LengthCompare) Func2; // 等價(jià)的類型
// FuncP1 和 FuncP2 是指向函數(shù)的指針
typedef bool (*FuncP1)(const std::string&, const std::string&);
typedef decltype (LengthCompare) *FuncP2; // 等價(jià)的類型
然后别威,可以使用如下方式改寫 useBigger:
void useBigger(const std::string &s1, const std::string &s2, Func1);
void useBigger(const std::string &s1, const std::string &s2, FuncP2);
返回指向函數(shù)的指針
與數(shù)組類似第献,雖然不能返回一個(gè)函數(shù),但是能返回指向函數(shù)類型的指針兔港。然而,我們必須把返回類型寫成指針形式衫樊,編譯器不會(huì)自動(dòng)地將函數(shù)返回類型當(dāng)成對(duì)應(yīng)的指針類型處理飒赃。要想聲明一個(gè)返回函數(shù)指針的函數(shù),最簡(jiǎn)單的方法是借助類型別名:
using F = int(int*, int); // F 是函數(shù)類型载佳,不是指針
using PF = int(*)(int*, int); // PF 是指針類型
我們必須顯式地將返回類型指定為指針:
PF f1(int); // 正確:PF 是指向函數(shù)的指針,f1 返回指向函數(shù)的指針
F f2(int); // 錯(cuò)誤:F 是函數(shù)類型,f2 不能返回一個(gè)函數(shù)
F *f3(int); // 正確
當(dāng)然黍析,我們也能用下面的形式直接地聲明 f1:
int (*f1(int))(int*, int);
另外,我們還可以使用尾置返回類型的方式:
auto f1(int)->int (*)(int*, int);
將 auto 和 decltype 用于函數(shù)指針類型
如果我們明確知道返回的函數(shù)是哪一個(gè)奄抽,就能使用 decltype 簡(jiǎn)化書(shū)寫函數(shù)指針?lè)祷仡愋偷倪^(guò)程蔼两。例如假定有兩個(gè)函數(shù),它們的返回類型都是 std::string::size_type逞度,并且各有兩個(gè) const std::string& 類型的形參额划,此時(shí)我們可以編寫第三個(gè)函數(shù),它接受一個(gè) std::string 類型的參數(shù)第晰,返回一個(gè)指針锁孟,該指針指向前兩個(gè)函數(shù)中的一個(gè):
std::string::size_type sumLength(const std::string&, const std::string&);
std::string::size_type largerLength(const std::string&, const std::string&);
// 根據(jù)其形參的取值彬祖,getFcn 函數(shù)返回指向 sumLength 或者 largerLength 的指針
decltype (sumLength) *getFcn(const std::string&);
聲明 getFcn 唯一需要注意的地方是茁瘦,牢記當(dāng)我們將 decltype 作用于某個(gè)函數(shù)時(shí),它返回函數(shù)類型而非指針類型储笑。因此甜熔,我們顯示地加上 * 以表明我們需要返回指針,而非函數(shù)本身突倍。