zoukankan      html  css  js  c++  java
  • 如何使用指向类的成员函数的指针(详解!)

    我们首先复习一下"指向函数的指针"如何使用?

    [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 ", &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. }  
  • 相关阅读:
    读书笔记——吴军《态度》
    JZYZOJ1237 教授的测试 dfs
    NOI1999 JZYZOJ1289 棋盘分割 dp 方差的数学结论
    [JZYZOJ 1288][洛谷 1005] NOIP2007 矩阵取数 dp 高精度
    POJ 3904 JZYZOJ 1202 Sky Code 莫比乌斯反演 组合数
    POJ2157 Check the difficulty of problems 概率DP
    HDU3853 LOOPS 期望DP 简单
    Codeforces 148D. Bag of mice 概率dp
    POJ3071 Football 概率DP 简单
    HDU4405 Aeroplane chess 飞行棋 期望dp 简单
  • 原文地址:https://www.cnblogs.com/lidabo/p/6639363.html
Copyright © 2011-2022 走看看