如何使用指向類的成員函數的指針(詳解!)


我們首先復習一下"指向函數的指針"如何使用?

 

[cpp]  view plain  copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. void print()  
  2. {  
  3. }  
  4. void (*pfun)(); //聲明一個指向函數的指針,函數的參數是 void,函數的返回值是 void  
  5. pfun = print;   //賦值一個指向函數的指針  
  6. (*pfun)();    //使用一個指向函數的指針  

 

微笑吐舌頭比較簡單,不是嗎?為什么*pfun需要用()擴起來呢?

微笑吐舌頭因為*的運算符優先級比()低,如果不用()就成了*(pfun()).

微笑吐舌頭指向類的成員函數的指針不過多了一個類的限定而已!

 

[cpp]  view plain  copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. class A  
  2. {  
  3. void speak(char *, const char *);   
  4. };  
  5.   
  6. void main()  
  7. {  
  8. A a;  
  9. void (A::*pmf)(char *, const char *);//指針的聲明  
  10. pmf = &A::speak; //指針的賦值  
  11. }  

 

微笑吐舌頭一個指向類A 成員函數的指針聲明為:

微笑吐舌頭void (A::*pmf)(char *, const char *);

微笑吐舌頭聲明的解釋是:pmf是一個指向A成員函數的指針,返回無類型值,函數帶有二個參數,參數的類型分別是char *和const char *。除了在星號前增加A::,與聲明外部函數指針的方法一樣。一種更加高明的方法是使用類型定義:例如,下面的語句定義了PMA是一個指向類A成成員函數的指針,函數返回無類型值,函數參數類型為char *和const char *:

委屈快哭了typedef void(A::*PMA)(char *,const char *);

委屈快哭了PMA pmf= &A::strcat;//pmf是 PMF類型(類A成員指針)的變量

委屈快哭了下面請看關於指向類的成員函數的使用示例:

 

[cpp]  view plain  copy
 
 print?在CODE上查看代碼片派生到我的代碼片
  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. class Person  
  5. {  
  6. public:  
  7.     /*這里稍稍注意一下,我將speak()函數設置為普通的成員函數,而hello()函數設置為虛函數*/  
  8.     int value;  
  9.     void speak()      
  10.     {  
  11.         cout << "I am a person!" << endl;  
  12.         printf ("%d\n", &Person::speak); /*在這里驗證一下,輸出一下地址就知道了!*/  
  13.     }  
  14.     virtual void hello()  
  15.     {  
  16.         cout << "Person say \"Hello\"" << endl;  
  17.     }  
  18.     Person()  
  19.     {  
  20.         value = 1;  
  21.     }  
  22.   
  23. };  
  24.   
  25.   
  26.   
  27. class Baizhantang: public Person  
  28. {  
  29. public:  
  30.     void speak()  
  31.     {  
  32.         cout << "I am 白展堂!" << endl;  
  33.     }  
  34.     virtual void hello()  
  35.     {  
  36.         cout << "白展堂 say \"hello!\"" << endl;  
  37.     }  
  38.     Baizhantang()  
  39.     {  
  40.         value = 2;  
  41.     }  
  42. };  
  43.   
  44. typedef void (Person::*p)();//定義指向Person類無參數無返回值的成員函數的指針  
  45. typedef void (Baizhantang::*q)();//定義指向Baizhantang類的無參數無返回值的指針  
  46.   
  47. int main()  
  48. {  
  49.     Person pe;  
  50.     int i = 1;  
  51.     p ip;  
  52.     ip = &Person::speak;    //ip指向Person類speak函數  
  53.     (pe.*ip)();     //這個是正確的寫法!  
  54.   
  55.     //--------------------------------------------  
  56.     //  result : I am a Person!   
  57.     //           XXXXXXXXXX(表示一段地址)  
  58.     //--------------------------------------------  
  59.   
  60.     /* 
  61.     *下面是幾種錯誤的寫法,要注意! 
  62.     *       pe.*ip(); 
  63.     *       pe.(*ip)(); 
  64.     *       (pe.(*ip))(); 
  65.     */  
  66.   
  67.     Baizhantang bzt;  
  68.       
  69.     q iq = (void (Baizhantang::*)())ip; //強制轉換  
  70.     (bzt.*iq)();  
  71.   
  72.     //--------------------------------------------  
  73.     //  result : I am a Person!  
  74.     //           XXXXXXXXXX(表示一段地址)  
  75.     //--------------------------------------------  
  76.   
  77.     /*  有人可能會問了:ip明明被強制轉換成了Baizhantang類的成員函數的指針,為什么輸出結果還是: 
  78.     * I am a Person!在C++里面,類的非虛函數都是采用靜態綁定,也就是說類的非虛函數在編譯前就已經 
  79.     *確定了函數地址!ip之前就是指向Person::speak函數的地址,強制轉換之后,只是指針類型變了,里面 
  80.     *的值並沒有改變,所以調用的還是Person.speak函數,細心的家伙會發現,輸出的地址都是一致的. 
  81.     *這里要強調一下:對於類的非靜態成員函數,c++編譯器會給每個函數的參數添加上一個該類的指針this,這也 
  82.     *就是為什么我們在非靜態類成員函數里面可以使用this指針的原因,當然,這個過程你看不見!而對於靜態成員 
  83.     *函數,編譯器不會添加這樣一個this。 
  84.     */  
  85.       
  86.     iq = &Baizhantang::speak;   /*iq指向了Baizhantang類的speak函數*/  
  87.     ip = (void (Person::*)())iq;    /*ip接收強制轉換之后的iq指針*/  
  88.     (bzt.*ip)();  
  89.   
  90.     //--------------------------------------------  
  91.     //  result : I am 白展堂!  
  92.     //--------------------------------------------  
  93.   
  94.     (bzt.*iq)();//這里我強調一下,使用了動態聯編,也就是說函數在運行是才確定函數地址!  
  95.   
  96.     //--------------------------------------------  
  97.     //  result : I am 白展堂!  
  98.     //--------------------------------------------  
  99.   
  100.     /*這一部分就沒有什么好講的了,很明白了!由於speak函數是普通的成員函數,在編譯時就知道 
  101.     *到了Baizhantang::speak的地址,因此(bzt.*ip)()會輸出“I am 白展堂!”,即使iq被強制轉換 
  102.     *成(void (Person::*)())類型的ip,但是其值亦未改變,(bzt.*iq)()依然調用iq指向處的函數 
  103.     *即Baizhantang::speak. 
  104.     */  
  105.   
  106.   
  107.     /*好了,上面講完了普通成員函數,我們現在來玩一點好玩的,現在來聊虛函數*/  
  108.     ip = &Person::hello;    /*讓ip指向Person::hello函數*/  
  109.     (pe.*ip)();  
  110.   
  111.     //--------------------------------------------  
  112.     //  result : Person say "Hello"  
  113.     //--------------------------------------------  
  114.   
  115.     (bzt.*ip)();  
  116.   
  117.     //--------------------------------------------  
  118.     //  result : 白展堂 say "Hello"  
  119.     //--------------------------------------------  
  120.   
  121.     /*咦,這就奇怪了,為何與上面的調用結果不類似?為什么兩個調用結果不一致?伙伴們注意了: 
  122.     *speak函數是一個虛函數,前面說過虛函數並不是采用靜態綁定的,而是采用動態綁定,所謂動態 
  123.     *綁定,就是函數地址得等到運行的時候才確定,對於有虛函數的類,編譯器會給我們添加一個指針 
  124.     *vptr,指向一個虛函數表vptl,vptl里面存放着虛函數的地址,子類繼承父類的時候,也會繼承這樣 
  125.     *一個指針,如果子類復寫了虛函數,那么該表中該虛函數地址將會由父類的虛函數地址替換成子類虛 
  126.     *函數地址,編譯器會把(pe.*ip)()轉化成為(pe.vptr[1])(pe),加上動態綁定,結果會輸出: 
  127.     *       Person say "Hello"    
  128.     *(bzt.*ip)()會被轉換成(bzt.vptr[1])(pe),自然會輸出: 
  129.     *       白展堂 say "Hello" 
  130.     *ps:這里我沒法講得更詳細,因為解釋起來肯定是很長很長的,感興趣的話,我推薦兩本書你去看一看: 
  131.     *   第一本是侯捷老師的<深入淺出MFC>,里面關於c++的虛函數特性講的比較清楚; 
  132.     *   第二本是侯捷老師翻譯的<深度探索C++對象模型>,一聽名字就知道,講這個就更詳細了; 
  133.     *當然,不感興趣的同學這段解釋可以省略,對與使用沒有影響! 
  134.     */  
  135.   
  136.     iq = (void (Baizhantang::*)())ip;  
  137.     (bzt.*iq)();  
  138.   
  139.     //--------------------------------------------  
  140.     //  result : 白展堂 say "Hello"  
  141.     //--------------------------------------------  
  142.       
  143.     system("pause");  
  144.     return 0;  
  145. }  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM