1. 程式人生 > >【C++學習筆記】函式匹配和函式指標

【C++學習筆記】函式匹配和函式指標

一、函式匹配
  在大多數情況下,比較容易確定某次呼叫應選用哪個過載函式,然而當幾個過載函式的形引數量相等以及某些形參的型別可以由其他型別轉換得到時,這個相對就不那麼容易。

1.1 實參型別轉換
  確定最佳匹配,編譯器將實參型別到形參型別的轉換分成以下幾個等級:
  1.精確匹配,包括:
   ①實參型別和形參型別相同;
   ②實參從陣列或函式型別轉換成對應的指標型別;
   ③向實參新增層const或者從實參中刪除頂層const。
  2.通過const轉換實現的匹配。
  3.通過型別提升實現的匹配。
  4.通過算術型別轉換或者指標轉換。
  5.通過類型別轉換實現的匹配。
  ①需要型別提升和算術型別轉換的匹配。

分析函式呼叫前,我們應該知道小證書一般都會提升到int型別或更大的整數型別。假設兩個函式,一個接受int、另一個接受short,則只有當呼叫提供的是short型別的值時才會選擇short版本的函式。有時即使實參是一個很小的整數值,也會直接將它提升成int型別,此時使用short版本反而會導致型別轉換:

void ff(int);
void ff(short);
ff('a');                          //char提升成int,呼叫ff(int)

  所有算術型別轉換的級別都一樣,例如從int向unsigned int的轉換並不比從int向double的轉換級別高。

void mainp(long);
void mainp(float);
mainp(3.14);                     //錯誤,二義性呼叫

1.2 函式匹配和const實參
  如果過載函式的區別在於它們的引用型別的形參是否引用了const,或者指標型別的形參是都指向const,則當呼叫發生時編譯器通過實參是否是常量來決定選擇哪個函式。

Record lookup(Account&);         //函式的引數是Account的引用
Record lookup(const Account&);   //函式的引數是一個常量引用
const Account a;
Account b;

lookup(a);                       //呼叫lookup(const Account&)
lookup(b); //呼叫loopup(Account&)

二、函式指標
  函式指標指向的是函式而非物件,函式的型別由它的返回型別和形參型別共同決定,與函式名無關.

bool lengthCompare(const string &,const string &);

  上述函式的型別是bool(const string & ,const string & ),要宣告一個可以指向該函式的指標,只需要用指標替換函式名即可。

//pf指向一個函式,該函式的引數是兩個const string的引用,返回值是bool型別
bool (*pf)(const string &, const string &);   //未初始化

  pf前面有個表示pf是指標,右側是形參列表,表示pf指向的是函式,函式的返回型別是布林值,所以pf是一個指向函式的指標,該函式的引數是兩個const string的引用,返回值是bool型別。需要注意的是*pf兩端的括號不可少,如果不寫這對括號,那麼表示pf是一個返回值為bool指標的函式,即函式返回bool

2.1 使用函式指標
  如果把函式名作為一個值使用,該函式自動地轉換成指標。如下間lengthCompare的地址賦給pf。

pf = lengthCompare;                  //pf指向名為lengthCompare的函式
pf = &lenghtCompare;                 //等價的賦值語句,取址符是可選的
bool b1 = pf("hello","goodbye");     //呼叫lengthCompare函式
bool b2 = (*pf)("hello","goodbye");  //一個等價的呼叫
bool b3 = lengthCompare("hello","goodbye");   //另一個等價的呼叫

2.2 過載函式的指標
  當我們使用過載函式時,上下文必須清晰地界定到底應該選用哪個函式,如果定義指向過載函式的指標,編譯器通過指標型別決定選用哪個函式,指標型別必須與過載函式中的某一個精確匹配:

void ff(int*);
void ff(unsigned int);
void (*pf1)(unsigned int) = ff;     //pf1指向ff(unsigned)

void (*pf2)(int) = ff;              //錯誤,沒有任何一個ff與該形參列表匹配
double (*pf3)(int*) = ff;          //錯誤:ff和pf3的返回型別不匹配

2.3 函式指標形參
  和陣列類似,雖然不能定義函式型別的形參,但是形參可以是指向函式的指標,這樣形參看起來是函式型別,實際是當成指標來使用:

//第三個形參是函式型別,它會自動地轉換成指向函式的指標
void useBigger(const string &s1,const string &s2,bool pf(const string &,const string &));
//等價的宣告,顯式地將形參定義成指向函式的指標
void useBigger(const string &s1,const string &s2,bool (*pf)(const string &,const string &));

2.4 返回指向函式的指標
  和陣列類似,雖然不能返回一個函式,但是能返回指向函式型別的指標,此外,必須把返回型別寫成指標形式,編譯器不會自動地將函式返回型別當成對應的指標型別處理。宣告一個返回函式指標的函式,最簡單的辦法是使用類型別名:

using F = int(int*,int);             //F是函式型別,不是指標
using PF = int(*)(int*,int);         //PF是指標型別
PF f1(int);                          //正確,PF是指向函式的指標,f1返回指向函式的指標
F f1(int);                           //錯誤,F是函式型別,f1不能返回一個函式
F *f1(int);                          //正確,顯式地指定返回型別是指向函式的指標
int (*f1(int))(int*,int);            //正確,直接宣告f1

參考文獻:
①C++ primer 第五版