C++ Primer Plus書之--C++函式指標
函式指標
使用函式指標, 能完成這樣的工作:可以編寫將另一個函式的地址作為引數的函式, 這樣第一個函式就能找到第二個函式, 並且執行它, 與直接呼叫第一個函式相比, 這種方法很笨拙, 但他允許在不同的時間傳遞不同函式的地址, 這意味著可以再不同的時間使用不同的函式.
基本需要包含以下步驟:
1.獲取函式的地址
2.宣告一個函式指標
3.使用函式指標來呼叫函式
第一步: 獲取函式的地址:
獲取函式地址很簡單, 只需要使用函式名(後面不要跟引數)即可. 例如:think 是一個函式
// 這樣就會把函式think的地址傳遞給函式process process(think); // 這樣是先呼叫think函式, 將think的返回值傳遞給process函式 thought(think());
第二步:宣告函式指標
宣告指向某種資料型別的指標時, 必須指定指標指向的型別. 同樣宣告指向函式的指標時, 也必須指定指標指向的函式型別. 這意味著宣告指定函式的返回型別以及函式的引數列表, 例如:
// 函式原型, 原型中可以不指定形參變數
double test(int);
// 正確的指標型別宣告如下:
// 意思是pfun指向一個需要一個int型引數的返回double型別的函式
double (*pfun)(int);
注意:與test()函式原型類似, 就是將test替換為(*pfun), 由於test是函式, 因此(*pfun)也是函式, 而如果(*pfun)是函式, 則pfun就是指向函式的指標.
通常, 要宣告指向特定型別的函式的指標的時候, 可以先寫這種函式的原型, 然後用(*pfun)替換函式名, 則pfun就是指向函式的指標.
注意: 必須在宣告中使用()將*pfun括起來, 括號的優先順序比*高, 因此*pfun(int), 表示pfun是一個返回指標的函式, 而(*pfun)(int)表示fpun是指向函式的指標.
正確宣告pfun後, 就可以將相應函式的地址賦給pfun:
pfun = test;
注意test()的引數列表和返回值, 必須和pfun的相同, 否則會報錯.
現在來寫一個完整函式呼叫另一個函式的函式宣告:
// 宣告estimate接收一個指向一個接收int型引數,並且返回double型別的函式的指標 void estimate(int lines, double (*pfun)(int)); // 使用estimate很簡單: // pam是函式名, 就是需要傳遞進函式estimate的函式的函式名 estimate(50, pam);
第三步:使用指標來呼叫函式.
前面說過(*pfun)與函式名的作用相同, 因此使用時只需將(*pfun)看做函式名即可.
// 這種寫法雖然不太好看, 但是明確的顯示出來, 正在使用的是函式指標
double x = (*pfun)(2);
// c++也支援這種寫法, 就像使用函式名那樣使用pfun
double y = pfun(3);
來看一個完整的例子:
// 函式指標例子
#include <iostream>
using namespace std;
// 函式原型
double betsy(int);
double pam(int);
// 第二個引數值一個函式指標, 指向一個需要一個int型引數, 並且返回double的函式
void estimate(int lines, double (*pfun)(int));
int main()
{
int code;
cout << "How many lines of code do you need? ";
cin >> code;
cout << "Here's Betsy's time : " << endl;
// 函式名就是函式的地址, 所以直接將函式名傳進去
estimate(code, betsy);
cout << "Here's pam's time : " << endl;
estimate(code, pam);
return 0;
}
double betsy(int lines)
{
return 0.02 * lines;
}
double pam(int lines)
{
return 0.03 * lines;
}
void estimate(int lines, double (*pfun)(int))
{
cout << lines << " lines will take : ";
// 或者寫成這樣也行: cout << pfun(lines) << " hours " << endl;
cout << (*pfun)(lines) << " hours " << endl;
}
看一下執行結果:
深入探討函式指標
1.宣告三個函式原型, 他們的引數列表和返回型別都相同:
const double * f1(const double arr[], int n);
const double * f2(const double [], int n);
const double * f3(const double *, int n);
const double arr[] 和 const double *arr相同而變數名可以省略, 因此上面三個函式原型的引數列表和返回值都一樣
2.宣告一個函式指標, 指向上面宣告的三種函式其中的任何一種
const double *(pf)(const double *, int);
並且可以在宣告的同事進行初始化:
const double *(pf)(const double *, int) = f1;
使用c++11的自動型別推斷功能時, 程式碼要簡單的多:
auto p2 = f2;
3.再來看下面這句:
cout << (*p1)(av, 3) << " : " << *(*p1)(av, 3) << endl;
cout << p2(av, 3) << " : " << *p2(av, 3) << endl;
(*p1)(av, 3)和p2(av, 3)都是呼叫指向的函式f1和f2, 並將av和3作為引數, 因此這兩個顯示的是函式的返回值, 也就是double值得地址, 而要顯示double值則使用後面的程式碼即*(*p1)(av, 3) 和 *p2(av, 3)
鑑於需要使用三個函式, 如果有一個函式的指標陣列將會很方便, 如何宣告這樣的陣列呢?
const double * (*pf[3])(const double *, int) = {f1, f2, f3};
為何要將[3]放在這個地方呢? pf是一個包含了三個元素的陣列, 因此要宣告這樣的陣列, 首先要pf[3], 該宣告的其他部分指出了陣列包含的元素是什麼樣的. 運算子[]的優先順序高於*, 因此*pf[3]是一個包含三個指標的陣列, 上述宣告的其他部分指出了每個指標指向的是什麼, 引數列表為const double *, int且返回值是double *的函式.
我們既然已經聲明瞭pa, 那麼再宣告同樣的陣列就可以使用atuo了
auto pb = pa;
因為陣列名是指向陣列的第一個元素的指標, 因此pb和pa都是指向函式指標的指標
可以使用如下的方法來呼叫函式:
const double * px = pa[0](test, 3);
const double * py = (*pb[1])(test, 3);
要獲得對應的double值, 可以使用運算子*
double x = *(pa[0](test, 3));
double y = *((*pb[1])(test, 3));
再看下面這種宣告:
auto pc = &pa;
如果我們想自己宣告pc的話, 需要這樣做:
// 這是一個指標, 指向了一個包含三個元素的陣列
(*pd)[3]
// 這是一個數組, 裡面包含了三個指標
*pd[3]
主要是涉及到優先順序的問題, *的優先順序比[]低, 所以第二個首先是個陣列, 然後前面的*指明瞭它裡面的內容是指標, 第一個首先是(*pd)說明是個指標, 然後[]說明指標指向了一個包含三個元素的陣列.
換句話說, pd是一個指標, 它指向一個包含三個元素的陣列:
const double* (*(*pd)[3])(double *, int) = &pa;
要呼叫函式, 需要明確的是既然pd是指向陣列, 那麼*pd就是陣列, 而(*pd)[i]就是陣列中的元素即函式指標, 因此簡單的呼叫方式是(*pd)[i](arr, 3), 而*(*pd)[i](arr, 3)就是返回的指標指向的值. 也可以使用指標呼叫函式的語法(*(*pd)[i])(arr, 3)來呼叫函式(按照面介紹的既然(*pd)[i], 相當於函式名, 那麼函式指標就可以按照在函式名前加*來呼叫函式, 也就是(*(*pd)[i])), *(*(*pd)[i])(arr, 3)就是指向的double的值.
下面看一下完整的例子
// 深究函式指標
#include <iostream>
using namespace std;
// 以三種不同的方式生命了三個函式原型, 實際他們的函式格式都是一樣的, 只是函式名不同
// 就是函式的引數列表和函式的返回值都是一樣的
const double* f1(const double arr[], int n);
const double* f2(const double [], int n);
const double* f3(const double *, int n);
int main()
{
double d_arr[3] = {1.23, 2.34, 3.45};
// 定義了一個函式指標
// 指向一個接收const double *, 和int型 兩個引數的, 並且返回double *的函式
// 然後把函式f1賦值給了p1這個指標
const double* (*p1)(const double *, int) = f1;
// 利用auto聲明瞭一個函式指標
// 這個函式指標實際和宣告p1的時候一樣, 只不過auto比較省事
auto p2 = f2;
// const double* (*p2)(const double *, int) = f2; 上面auto和這行意義一樣
cout << "Using pointers to functions: " << endl;
cout << "Address Value" << endl;
cout << p1(d_arr, 3) << " : " << *(p1(d_arr, 3)) << endl;
cout << (*p2)(d_arr, 3) << " : " << *((*p2)(d_arr, 3)) << endl;
// 聲明瞭一個包含三個指標的陣列
// 而數組裡的指標是指向接收const double *和int型引數, 並且返回double *的函式
// 這句話不能直接使用auto, auto不適用列表初始化
const double * (*pa[3])(const double *, int) = {f1, f2, f3};
// 但是仍然適用於單個值的初始化
// pb是一個指標, 指向pa的第一個元素
auto pb = pa;
// c++11 之前可以用下面這行, pb是一個指標的指標
// const double * (**pb)(const double *, int) = pa;
cout << "Using an array of pointers" << endl;
cout << "Anddress Value" << endl;
for (int i = 0; i < 3; i++)
cout << (pa[i])(d_arr, 3) << " : " << (*(pa[i])(d_arr, 3)) << endl;
cout << "Using a pointer to a pointer to a function : " << endl;
cout << "Anddress Value" << endl;
for(int i = 0; i < 3; i++)
cout << pb[i](d_arr, 3) << " : " << *pb[i](d_arr, 3) << endl;
cout << "Using pointers to an array of pointers: " << endl;
cout << "Address Value" << endl;
auto pc = &pa;
// const double * (*(*pc)[3])(const double *, int) = &pa;
cout << (*pc)[0](d_arr, 3) << " : " << *(*pc)[0](d_arr, 3) << endl;
const double * (*(*pd)[3])(const double *, int) = &pa;
const double * pdb = (*pd)[1](d_arr, 3);
cout << pdb << " : " << *pdb << endl;
cout << (*(*pd)[2])(d_arr, 3) << " : " << *(*(*pd)[2])(d_arr, 3) << endl;
return 0;
}
const double* f1(const double arr[], int n)
{
return arr;
}
const double* f2(const double arr[], int n)
{
return arr + 1;
}
const double* f3(const double * arr, int n)
{
return arr + 2;
}
看一下執行結果: