如何使用指向类的成员函数的指针(详解!)

简介: 我们首先复习一下"指向函数的指针"如何使用?   [cpp] view plain copy    print? void print()   {   }   void (*pfun)(); //声明一个指向函数的指针,函数的参数是 void,函数的返回值是 voi...

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

 

[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. }  
目录
相关文章
|
1月前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
60 4
|
1月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
129 4
|
2月前
|
存储 C语言 C++
如何通过指针作为函数参数来实现函数的返回多个值
在C语言中,可以通过将指针作为函数参数来实现函数返回多个值。调用函数时,传递变量的地址,函数内部通过修改指针所指向的内存来改变原变量的值,从而实现多值返回。
|
2月前
|
存储 搜索推荐 C语言
如何理解指针作为函数参数的输入和输出特性
指针作为函数参数时,可以实现输入和输出的双重功能。通过指针传递变量的地址,函数可以修改外部变量的值,实现输出;同时,指针本身也可以作为输入,传递初始值或状态。这种方式提高了函数的灵活性和效率。
|
2月前
利用指针函数
【10月更文挑战第2天】利用指针函数。
22 1
|
2月前
|
存储 编译器 C语言
C++入门2——类与对象1(类的定义和this指针)
C++入门2——类与对象1(类的定义和this指针)
54 2
|
2月前
|
算法 搜索推荐 C语言
【C语言篇】深入理解指针4(模拟实现qsort函数)
【C语言篇】深入理解指针4(模拟实现qsort函数)
29 2
|
3月前
|
Linux
在Linux内核中根据函数指针输出函数名称
在Linux内核中根据函数指针输出函数名称
|
4月前
|
程序员 C语言
指针在函数参数和返回值中的使用
指针在函数参数和返回值中的使用
73 9
|
4月前
|
编译器 C++
virtual类的使用方法问题之在C++中获取对象的vptr(虚拟表指针)如何解决
virtual类的使用方法问题之在C++中获取对象的vptr(虚拟表指针)如何解决
105 4