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

另外一篇英文参考:Member Function Pointers and the Fastest Possible C++ Delegates

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

[cpp] view plaincopy print?
  1. void print()
  2. {
  3. }
  4. void (*pfun)(); //声明一个指向函数的指针,函数的参数是 void,函数的返回值是 void
  5. pfun = print;   //赋值一个指向函数的指针
  6. (*pfun)();    //使用一个指向函数的指针

比较简单,不是吗?为什么*pfun需要用()扩起来呢?

因为*的运算符优先级比()低,如果不用()就成了*(pfun()).

指向类的成员函数的指针不过多了一个类的限定而已!

[cpp] view plaincopy print?
  1. class A
  2. {
  3. void speak(char *, const char *);
  4. };
  5. void main()
  6. {
  7. A a;
  8. void (A::*pmf)(char *, const char *);//指针的声明
  9. pmf = &A::speak; //指针的赋值
  10. }

一个指向类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 plaincopy print?
  1. #include <iostream>
  2. using namespace std;
  3. class Person
  4. {
  5. public:
  6. /*这里稍稍注意一下,我将speak()函数设置为普通的成员函数,而hello()函数设置为虚函数*/
  7. int value;
  8. void speak()
  9. {
  10. cout << "I am a person!" << endl;
  11. printf ("%d\n", &Person::speak); /*在这里验证一下,输出一下地址就知道了!*/
  12. }
  13. virtual void hello()
  14. {
  15. cout << "Person say \"Hello\"" << endl;
  16. }
  17. Person()
  18. {
  19. value = 1;
  20. }
  21. };
  22. class Baizhantang: public Person
  23. {
  24. public:
  25. void speak()
  26. {
  27. cout << "I am 白展堂!" << endl;
  28. }
  29. virtual void hello()
  30. {
  31. cout << "白展堂 say \"hello!\"" << endl;
  32. }
  33. Baizhantang()
  34. {
  35. value = 2;
  36. }
  37. };
  38. typedef void (Person::*p)();//定义指向Person类无参数无返回值的成员函数的指针
  39. typedef void (Baizhantang::*q)();//定义指向Baizhantang类的无参数无返回值的指针
  40. int main()
  41. {
  42. Person pe;
  43. int i = 1;
  44. p ip;
  45. ip = &Person::speak;    //ip指向Person类speak函数
  46. (pe.*ip)();     //这个是正确的写法!
  47. //--------------------------------------------
  48. //  result : I am a Person!
  49. //           XXXXXXXXXX(表示一段地址)
  50. //--------------------------------------------
  51. /*
  52. *下面是几种错误的写法,要注意!
  53. *       pe.*ip();
  54. *       pe.(*ip)();
  55. *       (pe.(*ip))();
  56. */
  57. Baizhantang bzt;
  58. q iq = (void (Baizhantang::*)())ip; //强制转换
  59. (bzt.*iq)();
  60. //--------------------------------------------
  61. //  result : I am a Person!
  62. //           XXXXXXXXXX(表示一段地址)
  63. //--------------------------------------------
  64. /*  有人可能会问了:ip明明被强制转换成了Baizhantang类的成员函数的指针,为什么输出结果还是:
  65. * I am a Person!在C++里面,类的非虚函数都是采用静态绑定,也就是说类的非虚函数在编译前就已经
  66. *确定了函数地址!ip之前就是指向Person::speak函数的地址,强制转换之后,只是指针类型变了,里面
  67. *的值并没有改变,所以调用的还是Person.speak函数,细心的家伙会发现,输出的地址都是一致的.
  68. *这里要强调一下:对于类的非静态成员函数,c++编译器会给每个函数的参数添加上一个该类的指针this,这也
  69. *就是为什么我们在非静态类成员函数里面可以使用this指针的原因,当然,这个过程你看不见!而对于静态成员
  70. *函数,编译器不会添加这样一个this。
  71. */
  72. iq = &Baizhantang::speak;   /*iq指向了Baizhantang类的speak函数*/
  73. ip = (void (Person::*)())iq;    /*ip接收强制转换之后的iq指针*/
  74. (bzt.*ip)();
  75. //--------------------------------------------
  76. //  result : I am 白展堂!
  77. //--------------------------------------------
  78. (bzt.*iq)();//这里我强调一下,使用了动态联编,也就是说函数在运行是才确定函数地址!
  79. //--------------------------------------------
  80. //  result : I am 白展堂!
  81. //--------------------------------------------
  82. /*这一部分就没有什么好讲的了,很明白了!由于speak函数是普通的成员函数,在编译时就知道
  83. *到了Baizhantang::speak的地址,因此(bzt.*ip)()会输出“I am 白展堂!”,即使iq被强制转换
  84. *成(void (Person::*)())类型的ip,但是其值亦未改变,(bzt.*iq)()依然调用iq指向处的函数
  85. *即Baizhantang::speak.
  86. */
  87. /*好了,上面讲完了普通成员函数,我们现在来玩一点好玩的,现在来聊虚函数*/
  88. ip = &Person::hello;    /*让ip指向Person::hello函数*/
  89. (pe.*ip)();
  90. //--------------------------------------------
  91. //  result : Person say "Hello"
  92. //--------------------------------------------
  93. (bzt.*ip)();
  94. //--------------------------------------------
  95. //  result : 白展堂 say "Hello"
  96. //--------------------------------------------
  97. /*咦,这就奇怪了,为何与上面的调用结果不类似?为什么两个调用结果不一致?伙伴们注意了:
  98. *speak函数是一个虚函数,前面说过虚函数并不是采用静态绑定的,而是采用动态绑定,所谓动态
  99. *绑定,就是函数地址得等到运行的时候才确定,对于有虚函数的类,编译器会给我们添加一个指针
  100. *vptr,指向一个虚函数表vptl,vptl里面存放着虚函数的地址,子类继承父类的时候,也会继承这样
  101. *一个指针,如果子类复写了虚函数,那么该表中该虚函数地址将会由父类的虚函数地址替换成子类虚
  102. *函数地址,编译器会把(pe.*ip)()转化成为(pe.vptr[1])(pe),加上动态绑定,结果会输出:
  103. *       Person say "Hello"
  104. *(bzt.*ip)()会被转换成(bzt.vptr[1])(pe),自然会输出:
  105. *       白展堂 say "Hello"
  106. *ps:这里我没法讲得更详细,因为解释起来肯定是很长很长的,感兴趣的话,我推荐两本书你去看一看:
  107. *   第一本是侯捷老师的<深入浅出MFC>,里面关于c++的虚函数特性讲的比较清楚;
  108. *   第二本是侯捷老师翻译的<深度探索C++对象模型>,一听名字就知道,讲这个就更详细了;
  109. *当然,不感兴趣的同学这段解释可以省略,对与使用没有影响!
  110. */
  111. iq = (void (Baizhantang::*)())ip;
  112. (bzt.*iq)();
  113. //--------------------------------------------
  114. //  result : 白展堂 say "Hello"
  115. //--------------------------------------------
  116. system("pause");
  117. return 0;
  118. }

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3
 4 #define debug(x) cout << #x << " at line " << __LINE__ << " is: " << x << endl
 5
 6 class LuggageCompartment  {
 7 private:
 8     int m_iLuggage;    //私有变量,保存现在的行李总数
 9 public:LuggageCompartment() {
10         m_iLuggage = 0;
11     }            //构造函数
12     int TakeoutLuggage()    //取出一件行李
13     {
14         if (m_iLuggage != 0)
15             m_iLuggage--;
16         return m_iLuggage;
17     }
18     int InsertLuggage()    //放入一件行李
19     {
20         return (++m_iLuggage);
21     }
22     int checkLuggage()    //检查行李总数
23     {
24         return m_iLuggage;
25     }
26 };
27
28 class FlightSegment  {
29 private:LuggageCompartment * m_pLC;    //成员指针
30 public:void SetLuggageCompartment(LuggageCompartment * pLC)  {
31         m_pLC = pLC;
32     }            //设置成员指针
33     FlightSegment()    //构造函数将成员指针初始化为null
34     {
35         m_pLC = NULL;
36     }
37     int checkLuggage()  {
38         if (m_pLC == NULL)
39             return -1;
40         return m_pLC->checkLuggage();    //将函数调用委托给成员指针
41     }
42 };
43
44
45
46 int main(int argc, char *argv[])
47 {
48     FlightSegment segment;
49     LuggageCompartment lc1, lc2;
50     for (int i = 0; i < 10; i++)
51         lc1.InsertLuggage();
52     segment.SetLuggageCompartment(&lc1);
53     cout << "Now we have " << segment.checkLuggage()
54          <<" Luggages." << endl;
55     segment.SetLuggageCompartment(&lc2);
56     cout << "Now we have " << segment.checkLuggage()
57          <<" Luggages." << endl;
58     return 0;
59 }

View Code

转载于:https://www.cnblogs.com/guxuanqing/p/7791758.html

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

  1. C++中的const成员函数(函数声明后加const,或称常量成员函数)用法详解

  2. 指向类成员/函数的指针

    C++扩展了指针在类中的使用,使其可以指向类成员,这种行为是类层面的,而不是对象层面的. 指向类成员/函数的指针的本质并不是取地址.而是利用了对象地址的偏移量 我们创建了一个类,假设我们要使用指针指向 ...

  3. C++之指向对象成员函数的指针

    定义指向对象成员函数的指针变量的方法和定义指向普通函数指针变量方法有所不同: 1.普通指针函数变量的定义方法:数据类型名(*指针变量名)(参数列表): 例:void  (*p)(void); p = ...

  4. 怎么将一个类的成员函数作为指针传递给另一个类的成员函数

    今天帮同学解决了一个问题,怎么把一个类的成员函数作为指针传递给另一个类的成员函数. 以前只接触过C语言中的函数指针: #include <iostream.h> void add(int ...

  5. C++中,如何定义和使用指向成员函数的指针

    /** * 定义指向成员函数的指针变量的形式 : 成员函数返回类型 (类名∷*指针变量名)(参数列表) * 成员函数指针变量值的形式 : &类名∷成员函数名; * 成员函数指针变量使用形式 : ...

  6. C++教程:指向成员函数的指针

    转载自:https://kelvinh.github.io/blog/2014/03/27/cpp-tutorial-pointer-to-member-function/ Kelvin的胡言乱语 = ...

  7. C++57个入门知识点_40 常成员函数(用于定义不可修改类内部成员变量的函数,一般用来修饰Get函数;常成员函数this指针:const T* const;常成员函数内部变量修改方法:强转/关键字)

    前面我们已经学习了C++中重要的知识点,特别是虚函数可能会有些懵逼,但是需要我们在实践中不断的理解和尝试,写代码是进步最快的方式,接下来将会介绍一些简单但很重要的知识点,本篇介绍常成员函数. 总结: ...

  8. C语言结构体中定义函数指针详解

    C语言结构体中定义函数指针详解 结构体指针函数应用场景之一--驱动程序编写 结构体的一些基本用法 形式1:先定义结构体类型,再定义变量 形式2:在定义类型的同时定义变量 形式3:直接定义变量,用无名结 ...

  9. C++Study 指针详解

    C++指针详解 指针的概念 指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址.要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区, ...

最新文章

  1. Linux创建线程时 内存分配的那些事
  2. Uvalive 4043 - Ants(二分图完美匹配)
  3. js 强校验 弱校验_还在手写表单校验逻辑?试试spring validation吧
  4. Android Studio无法找到tool.jar解决方法!
  5. 快速傅里叶变换(FFT)相关内容汇总
  6. 【操作系统/OS笔记06】虚拟内存、覆盖、交换
  7. 数据库期末总结笔记( 零基础 )-第二章 关系数据库
  8. C#判断检测网络是否连接
  9. 并发控制技术手段之时间戳(二)
  10. android 蓝牙 发送字符串,Android向TLSR8266蓝牙mesh发送指令
  11. 用友NC系统安装部署指南
  12. Android控件 - TextView、Button、EditText、CompoundButton、CheckBox简介
  13. 将动态IP切换为静态
  14. python将.tif格式图批量转化为.jpg格式图
  15. 计算机基础知识论文统一格式,大一计算机基础知识论文.docx
  16. Mysql从入门到入魔——3. 查询、排序、WHERE过滤
  17. 刘新华老师-沪师经纪
  18. DNA转换为C语言,DNA (C语言代码)
  19. 1024,程序员“赚钱”秘籍倾囊而赠!
  20. 每次阅读外文技术资料都头疼,终于知道原因了。

热门文章

  1. 《scikit-learn》决策树之回归树
  2. 漫步线性代数二十五——特征值和特征向量
  3. oracle中表空间详解
  4. nlp论文——《Efficient Estimation of Word Representations in Vector Space》(向量空间中词表示的有效估计)
  5. 【例题+习题】【数值计算方法复习】【湘潭大学】(五)
  6. 如何使用CCRenderTexture创建动态纹理 Cocos2d-x 2 1 4
  7. 数据竞赛入门-金融风控(贷款违约预测)五、模型融合
  8. Transform的normalize参数含义
  9. STM32F103 - CubeMX 的使用实例详细(04.5)- STM32F103的 - 定时器设定详细解释 - 定时器相关的HAL接口函数 - 定时器的中断
  10. 5调色板怎么打开_CAD打开较大的图纸就卡死的解决方法