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

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

[cpp] view plain copy

print?

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

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

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

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

[cpp] view plain copy

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 plain copy

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. }
时间: 2024-11-13 01:38:35

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

c++: 指向类成员函数的指针

指向函数的指针有和指向类成员函数的指针的有什么不同? int f(char a, float b);如果它是普通函数,则它的类型是 int (*)(char, float);如果它是非静态成员函数,则它的类型是 int(ClassName::*)(char, float);如果它是静态成员函数,则它的类型是和普通函数一样. 如何把一个类的成员函数传递给event handler, 线程启动函数等? 类的成员函数必须和一个类的实例结合起来才有意义.可以写一个函数来包装类的成员函数. class X

指向类成员函数的指针

//类中的普通成员变量,类名::变量名,在栈上 //类名 *指针名=new 类名,在堆上 //类的静态成员变量,在静态区 //函数都在代码区,类的函数.静态函数都是共享的 void(myclass::*p1)() = myclass::run;//指向类成员函数的指针 void(*p2)() = myclass::go;//指向静态成员函数的指针 1 #include <iostream> 2 3 class myclass 4 { 5 public: 6 int num; 7 int dat

类的成员函数的指针

前面一篇文章<函数的指针 >介绍了全局函数的指针,现在我们再来介绍一下成员函数的指针. 成员函数指针的定义: 一般形式 Return_Type (Class_Name::* pointer_name) (Argument_List); 用typedef简化的形式 Typedef Return_Type (Class_Name::* FuncPtr_Type) (Argument_List); FuncPtr_Type pFunc = NULL; //pFunc为成员函数指针的指针变量 成员函数

类成员函数的指针与多态性

1 类成员函数的指针 2 类成员函数的指针数组 3 指向类成员函数的指针的指针 1 类成员函数的指针 auto func1 = &com::jia;//C++函数指针 int (com::*p)(int, int) = &com::jia;//C函数指针 1 #include <iostream> 2 3 class com 4 { 5 private: 6 int a; 7 int b; 8 public: 9 com(int x, int y) :a(x), b(y) 10

我的一点关于把WndProc指向类的成员函数的看法

我以前经常考虑使用WNDCLASS.CreateThread之类的Windows API时如何在类里面调用,传入函数参数地址时如何使用成员函数的地址.为了研究,写了一个示例代码: #include <iostream> #include <stdio.h> using namespace std; typedef int (__stdcall *XSSH_SAY_HELLO)(int); class XTestMethod { typedef int (__stdcall XTes

1 函数指针详解

今天第一次发一下关于C++的文章,主要是最近做的项目要用到boost::asio作为网络库,而boost::asio要用到很多的boost::bind函数,而boost::bind又要用到自由函数指针和成员函数指针,所以这一串下来,我首先就是要讲讲函数指针这个东东. 一,自由函数指针 这个自由函数应该就是对应成员函数的那种不在类中的函数吧,英语叫做free function,我是硬翻的,欢迎同学们指正打脸.它的指针很容易写,分为两种方法吧: (一)先声明函数指针类型,再定义函数指针实例. 比如,

C++函数指针详解

学习c++的过程中,指针是难点,熟悉了指针之后,还有一个让人很蛋疼的难点,那就是函数指针了.本博文详细介绍一下常见的各种坑爹的函数指针. 至于指针的详细学习,推荐这篇博文C++指针详解 与数据一样,函数也有地址,函数的地址就是内存中存放函数语言代码的起始地址.函数指针就是指向这个地址.函数指针所指向的类型,就是函数本身.我们知道,指针所指向类型代表了指针所指向的内存区域的大小.所以函数指针所指向的类型,就是函数在内存中所占据内存的大小.知道了函数的起始地址和大小,所以函数指针可以很轻易的代替函数

指针数组,数组指针,指针函数,函数指针,二级指针详解

先看个简单的:char *p,这定义了一个指针,指针指向的数据类型是字符型,char  *(p)定义了一个指针P: char *p[4], 为指针数组,由于[]的优先级高于*,所以p先和[]结合,p[]是一个数组,暂时把p[]看成是q,也就是char *(q),定义了一个指针q,只不过q是一个数组罢了,故定义了一个数组,数组里面的数据是char *的,所以数组里面的数据为指针类型.所以char *p[4]是四个指针,这四个指针组成了一个数组,称为指针数组,既有多个指针组成的数组. char(*p

C++Study 指针详解

C++指针详解 指针的概念 指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址.要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区.让我们分别说明. 先声明几个指针放着做例子: 例一: int *ptr; char *ptr; int **ptr; int (*ptr)[3]; int *(*ptr)[4]; 指针的类型 从语法的角度看,你只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类