指向成员函数的函数指针

转载:https://blog.csdn.net/lishuhuakai/article/details/18276477#

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

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

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

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

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

  class A
  {
  void speak(char *, const char *); 
  };
  
  void main()
  {
  A a;
  void (A::*pmf)(char *, const char *);//指针的声明
  pmf = &A::speak; //指针的赋值
  }

一个指向类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成员指针)的变量

下面请看关于指向类的成员函数的使用示例:

#include <iostream>
using namespace std;
 
class Person
{
public:
    /*这里稍稍注意一下,我将speak()函数设置为普通的成员函数,而hello()函数设置为虚函数*/
    int value;
    void speak()    
    {
        cout << "I am a person!" << endl;
        printf ("%d\n", &Person::speak); /*在这里验证一下,输出一下地址就知道了!*/
    }
    virtual void hello()
    {
        cout << "Person say \"Hello\"" << endl;
    }
    Person()
    {
        value = 1;
    }
 
};
 
 
 
class Baizhantang: public Person
{
public:
    void speak()
    {
        cout << "I am 白展堂!" << endl;
    }
    virtual void hello()
    {
        cout << "白展堂 say \"hello!\"" << endl;
    }
    Baizhantang()
    {
        value = 2;
    }
};
 
typedef void (Person::*p)();//定义指向Person类无参数无返回值的成员函数的指针
typedef void (Baizhantang::*q)();//定义指向Baizhantang类的无参数无返回值的指针
 
int main()
{
    Person pe;
    int i = 1;
    p ip;
    ip = &Person::speak;    //ip指向Person类speak函数
    (pe.*ip)();        //这个是正确的写法!
 
    //--------------------------------------------
    //    result : I am a Person! 
    //             XXXXXXXXXX(表示一段地址)
    //--------------------------------------------
 
    /*
    *下面是几种错误的写法,要注意!
    *        pe.*ip();
    *         pe.(*ip)();
    *        (pe.(*ip))();
    */
 
    Baizhantang bzt;
    
    q iq = (void (Baizhantang::*)())ip;    //强制转换
    (bzt.*iq)();
 
    //--------------------------------------------
    //    result : I am a Person!
    //             XXXXXXXXXX(表示一段地址)
    //--------------------------------------------
 
    /*    有人可能会问了:ip明明被强制转换成了Baizhantang类的成员函数的指针,为什么输出结果还是:
    * I am a Person!在C++里面,类的非虚函数都是采用静态绑定,也就是说类的非虚函数在编译前就已经
    *确定了函数地址!ip之前就是指向Person::speak函数的地址,强制转换之后,只是指针类型变了,里面
    *的值并没有改变,所以调用的还是Person.speak函数,细心的家伙会发现,输出的地址都是一致的.
    *这里要强调一下:对于类的非静态成员函数,c++编译器会给每个函数的参数添加上一个该类的指针this,这也
    *就是为什么我们在非静态类成员函数里面可以使用this指针的原因,当然,这个过程你看不见!而对于静态成员
    *函数,编译器不会添加这样一个this。
    */
    
    iq = &Baizhantang::speak;    /*iq指向了Baizhantang类的speak函数*/
    ip = (void (Person::*)())iq;    /*ip接收强制转换之后的iq指针*/
    (bzt.*ip)();
 
    //--------------------------------------------
    //    result : I am 白展堂!
    //--------------------------------------------
 
    (bzt.*iq)();//这里我强调一下,使用了动态联编,也就是说函数在运行是才确定函数地址!
 
    //--------------------------------------------
    //    result : I am 白展堂!
    //--------------------------------------------
 
    /*这一部分就没有什么好讲的了,很明白了!由于speak函数是普通的成员函数,在编译时就知道
    *到了Baizhantang::speak的地址,因此(bzt.*ip)()会输出“I am 白展堂!”,即使iq被强制转换
    *成(void (Person::*)())类型的ip,但是其值亦未改变,(bzt.*iq)()依然调用iq指向处的函数
    *即Baizhantang::speak.
    */
 
 
    /*好了,上面讲完了普通成员函数,我们现在来玩一点好玩的,现在来聊虚函数*/
    ip = &Person::hello;    /*让ip指向Person::hello函数*/
    (pe.*ip)();
 
    //--------------------------------------------
    //    result : Person say "Hello"
    //--------------------------------------------
 
    (bzt.*ip)();
 
    //--------------------------------------------
    //    result : 白展堂 say "Hello"
    //--------------------------------------------
 
    /*咦,这就奇怪了,为何与上面的调用结果不类似?为什么两个调用结果不一致?伙伴们注意了:
    *speak函数是一个虚函数,前面说过虚函数并不是采用静态绑定的,而是采用动态绑定,所谓动态
    *绑定,就是函数地址得等到运行的时候才确定,对于有虚函数的类,编译器会给我们添加一个指针
    *vptr,指向一个虚函数表vptl,vptl里面存放着虚函数的地址,子类继承父类的时候,也会继承这样
    *一个指针,如果子类复写了虚函数,那么该表中该虚函数地址将会由父类的虚函数地址替换成子类虚
    *函数地址,编译器会把(pe.*ip)()转化成为(pe.vptr[1])(pe),加上动态绑定,结果会输出:
    *       Person say "Hello"   
    *(bzt.*ip)()会被转换成(bzt.vptr[1])(pe),自然会输出:
    *        白展堂 say "Hello"
    *ps:这里我没法讲得更详细,因为解释起来肯定是很长很长的,感兴趣的话,我推荐两本书你去看一看:
    *    第一本是侯捷老师的<深入浅出MFC>,里面关于c++的虚函数特性讲的比较清楚;
    *    第二本是侯捷老师翻译的<深度探索C++对象模型>,一听名字就知道,讲这个就更详细了;
    *当然,不感兴趣的同学这段解释可以省略,对与使用没有影响!
    */
 
    iq = (void (Baizhantang::*)())ip;
    (bzt.*iq)();
 
    //--------------------------------------------
    //    result : 白展堂 say "Hello"
    //--------------------------------------------
    
    system("pause");
    return 0;
}

 

==========================================================

上面是大神的分析,

下面写一下自己遇到的场景:

typedef void(A::*PMA)(char *, const char *);
class B {

public:
    B(PMA func)
    {
        m_ProcFunc = func;  
    }
    const PMA PathProcFunc()
    {
        return m_ProcFunc;
    }

private:
    PMA m_ProcFunc;
};


//A类不给出了
void A::func()
{
    PMA pmf = B->PathProcFunc();  //B->getFunc()会返回一个  PMA类型的指针

    this->*pmf(....);
}

 

代码大概是这样,开始没明白为什么   this->*pmf(....) 可以执行呢?  A类中没有成员函数直接  初始化个给  PMA pmf 啊,而且还是用的 B类里的成员函数。

看了B类的构造函数后就明白了, 在B类构造对象的时候, 传入的函数PMA类型的(即,A类的成员函数),那么这个指针就一直是指向 A的成员函数的。 

心得体会:
指针的内容是重要的,指针出现在哪里并不重要!!

 

posted @ 2019-03-06 16:25  ren_zhg1992  阅读(559)  评论(0)    收藏  举报