[C++基礎] 指針函數與函數指針

1. 前言

   關於指針函數和函數指針,特別是函數指針,相信很多C/C++ers跟我曾經一樣,對它抱有敬畏,認爲它是很高深的東西,其實不然。要理解它花不了多少功夫,或許我一句話就能說清楚二者的區別,但是這樣也只是在腦子裏形成一個概念而已。大學時代,作爲一名學生時,我可以一天看完毛概,考八九十分;但是我用了一個星期去看譚浩強的C++教材(儘管現在很多人鄙視這本教材),上機時卻仍無從下手,我可以侃侃而談,熟悉一切概念,但是就是編不出程序。這就是程序員的世界,凡事只有動手才能領悟真諦。不過這也應證了一句千古名句,也是我最喜歡的一句詩“紙上得來終覺淺,絕知此事要躬行”。

   本文所有代碼編譯及運行環境:windows 7 professionnal, Visual Studio2010 professional.

2. 概述

   按照行文的總-分-總的結構,這裏仍然先概括的介紹一下指針函數和函數指針的概念,然後再用程序來詳細的介紹二者。下面就是指針函數和函數指針的概念。

  【指針函數】:返回指針的函數。重點是它是一個函數,只是返回值由普通的值或對象變成了指針,也就是說這個函數返回的是一塊內存的地址。

  【函數指針】:指向函數的指針。重點是它是一個指針,只是它指向的內容由普通的變量或對象變成了函數,也就是說它可以指向函數的入口地址。

3. 指針函數

   在介紹指針函數之前,我們先來看一個普通的函數。

複製代碼
 1 #include <iostream>
 2 using namespace std;
 3 
 4 class MyType{
 5 public:
 6     MyType(int value):m_value(value){
 7         cout<<"Construct."<<endl;
 8     }
 9     ~MyType(){
10         cout<<"Desconstruct."<<endl;
11     }
12 public:
13     int m_value;
14 };
15 
16 MyType getInstanceOfMyType(){
17     MyType mt(10);
18     cout<<&mt<<endl;
19     return mt;
20 }
21 
22 int main(){
23 
24     MyType mt = getInstanceOfMyType();
25     cout<<&mt<<endl;
26     cout<<mt.m_value<<endl;
27 
28     system("pause");
29     return 0;
30 }
複製代碼

   塗色部分就是我們需要注意的地方,函數"getInstanceOfMyType()"內部創建了一個MyType的對象,接着輸出了該對象的地址,最後返回了該對象。main函數裏面,通過調用該函數,獲得了函數的返回值,接着打印了返回對象的地址,再輸出獲得對象的m_value屬性的值。輸出結果如下:

Construct.
0045F688
Desconstruct.
0045F788
10
請按任意鍵繼續. . .

   可以看到,在"getInstanceOfMyType()"函數裏,對象創建之後又被銷燬了。從輸出可以看出,返回的對象地址與函數裏創建的對象地址是不一樣的,但是屬性m_value的值是一樣的,這說明通過該普通函數獲取的是函數內部創建對象的一個副本,這就是普通函數在返回對象時的處理。

   在看到普通函數的處理之後,我們再來看一個指針函數的處理,下面是一段指針函數的代碼,注意,這段代碼與上一段很相似,要注意區分。

複製代碼
 1 #include <iostream>
 2 using namespace std;
 3 
 4 class MyType{
 5 public:
 6     MyType(int value):m_value(value){
 7         cout<<"Construct."<<endl;
 8     }
 9     ~MyType(){
10         cout<<"Desconstruct."<<endl;
11     }
12 public:
13     int m_value;
14 };
15 
16 MyType *getInstanceOfMyType(){
17     MyType *mt = new MyType(10);
18     cout<<mt<<endl;
19     return mt;
20 }
21 
22 int main(){
23 
24     MyType *mt = getInstanceOfMyType();
25     cout<<mt<<endl;
26     cout<<mt->m_value<<endl;
27 
28     system("pause");
29     return 0;
30 }
複製代碼

   上面代碼着色的部分需要我們注意,特別是函數"getInstanceOfMyType()",它在這裏已經是一個指針函數了,那麼,這段程序的輸出是什麼呢?如下:

Construct.
00754AA8
00754AA8
10
請按任意鍵繼續. . .

   可以看出,在函數"getInstanceOfMyType()"中的對象一直沒有被調用析構函數,函數內和函數外的對象的地址是完全一樣的,當然,對象裏存儲的內容m_value的值也是一樣的。你可能會問,不是說函數調用完,就銷燬局部變量嗎?是的,它銷燬了,但是它只銷毀了"MyType *mt"這個指針,它指向的內存卻不會被銷燬。所以,在外面我們仍然可以繼續訪問這個對象。這種情況下,我們一般是需要在函數調用外面加上我們自己的delete操作的,上面的程序沒有添加這樣的操作,嚴格上來講是一個錯誤的程序。

   使用指針函數時,直接返回函數內部對象的地址,這樣就無需重新制造對象的副本,對效率的提升有幫助。但是需要注意的是,一定要記得在函數外部將函數內部申請的內存釋放掉,否則就有內存溢出的風險。

4. 函數指針

   下面說道我們今天主要的話題了——函數指針。函數指針是一個很有用的技術,它使得我們可以通過指針就能執行某一個函數代碼。對於技術高超的人來說,它是一把【絕世好劍】,能夠解決很多問題。下面,我們就函數指針來探究一番。

   首先,來看一段最簡單的函數指針的代碼,注意聲明和調用的方式。

複製代碼
 1 #include <iostream>
 2 using namespace std;
 3 
 4 int printFunc(int value){
 5     cout<<"this is a print function. the value is:"<<value<<endl;
 6     return 0;
 7 }
 8 
 9 int main(){
10 
11     int (*pFunction)(int x); // 這是一個函數指針變量
12     pFunction = printFunc;   // 這裏將函數入口地址給函數指針
13     (*pFunction)(7);         // 通過*運算符獲取了函數,再傳入參數7執行了函數
14 
15     system("pause");
16     return 0;
17 }
複製代碼

   上述代碼着色部分就是函數指針的聲明-定義-執行的過程,可以看出來,我只要將函數入口地址給函數指針就可以執行函數了。這裏有個知識點,就是關於函數的調用方式,一般我們調用函數的方式是這樣的:函數名(參數列表),但是其實函數的調用方式也可以這樣來寫:(*函數名)(參數列表)。即可以如下來調用函數,只是很少這樣用:

1 (*printFunc)(8);

   除此之外,我們還可以這樣寫:(&函數名)(參數列表)。即如下調用函數,這也幾乎沒人這樣用:

1 (&printFunc)(8);

   對於函數指針,它有兩個前提:①.就是指向的函數返回值要與聲明的函數指針一致。②.指向的函數的參數類型及個數要與聲明的函數指針一致。否則,是無法編譯通過的。

5. 函數指針類型

   上面一節在使用函數指針的時候,直接聲明瞭一個函數指針。其實函數指針也可以藉助typedef聲明爲一個類型,這樣我們就可以像定義int型變量一樣來定義一個函數指針了。定義函數指針類型代碼如下:

複製代碼
 1 #include <iostream>
 2 using namespace std;
 3 
 4 int printFunc(int value){
 5     cout<<"this is a print function. the value is:"<<value<<endl;
 6     return 0;
 7 }
 8 typedef int (*PFunction)(int x); // 函數指針類型,注意返回值和參數列表
 9 
10 int main(){
11 
12     PFunction ptrFunc; // 定義函數指針變量
13     ptrFunc = printFunc;
14     (*ptrFunc)(1); // 第一種調用方式
15     ptrFunc(2);    // 第二種調用方式
16 
17     system("pause");
18     return 0;
19 }
複製代碼

6. 一個函數指針的妙用示例

複製代碼
 1 #include <iostream>
 2 using namespace std;
 3 
 4 typedef void (*PFunction)(int x); // 函數指針類型,注意返回值和參數列表
 5 
 6 void printA(int value){
 7     cout<<"A - "<<value<<endl;
 8 }
 9 
10 void printB(int value){
11     cout<<"B - "<<value<<endl;
12 }
13 
14 void printC(int value){
15     cout<<"C - "<<value<<endl;
16 }
17 
18 int main(){
19     int choice;
20     PFunction ptrFunc[3] = {printA, printB, printC};
21     cin>>choice;
22     ptrFunc[choice](choice);
23 
24     system("pause");
25     return 0;
26 }
複製代碼

   具體的這裏就不解說了,代碼很短,也很容易看懂。

7. 結語

   本文就指針函數和函數指針做了一個簡單的入門講解,希望讀者在閱讀完本文以後,對指針函數和函數指針有一個深入的認識。當然,寫作本文的目的也是爲了強化筆者的C++基礎功底。



http://www.cnblogs.com/alephsoul-alephsoul/archive/2012/10/19/2730337.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章