函数指针和类成员指针

MFC利用函数指针和指向类成员指针来实现某些操作,有时让初学者感到困惑。例如串口回调函数等。
1 函数指针
      函数在内存中有确定的物理地址,可以将该地址赋给指针。这是因为函数在编译时,它的源代码转换成了目标代码,同时去定了函数的入口地址。程序调用函数,也就是指向这个入口地址。因此,指向函数的指针实际上包含了函数的入口地址,所以赋给指针的地址就是函数的入口地址,指针也就是可以用来代替函数名。于是可以将函数作为实参传递给其他函数,也就可以将一个指向函数的指针传递给函数,或放置在数组中提供给其他对象使用。
      1.1 函数指针定义
      函数指针定义的形式如下:
      数据类型标识符 (*指针对象名)(函数参数的数据类型列表);
      例如,语句“int (*p)(int,int);”仅仅说明定义的p是一个指向函数的指针,此函数返回整型值。p并没有固定指向某一个函数,而只是表示定义了一个类型的对象,专门用来存放函数的入口地址。在程序中把哪一个函数的地址赋给它,它就指向哪一个函数。在一个程序中,一个函数指针对象可以先后指向不同的函数。从这一点上看,它跟过去介绍的指针对象具有相同的性质。声明函数指针时,给出函数参数的数据类型即可,不需要给出参数名。如果给出,编译系统也不予理睬,所以下面两种形式是等效的:
      int(*p)(int,int);      //只给参数类型
      int(*p)(int a,int a);    //给出类型和参数名
也可以使用typedef定义,例如:
      typedef int (*FUN)(int a,int b);
      FUN p;
      则对象p为一个指向原型为int(int,int)的函数的指针。
      2.2 函数指针对象赋值
      给函数指针对象赋值时,只需要给出函数名而不必给出参数。因为语句
      p=函数名;
的作用是将函数入口地址赋给指针对象p,而不涉及实参与形参的结合问题。
      数组名代表数组的起始地址,这里的“函数名”则代表函数的入口地址。因为在C++中,单独一个函数名(其后不跟一个圆括号)会被自动地转换为指向该函数的指针(函数的第一条指令地址)。当函数地址被置入一个指针对象中时,可以通过该指针对象调用该函数。这里的p就是指向函数的入口处,而不是指向函数中间的某条具体指令。因此,*(p+1)、p+n、p--及p++等运算对它都是没有意义的。
      3. 调用指向函数
      调用指向函数的格式如下:
      (*指针对象名)(函数实参表);
      函数指针对象调用函数时,只需要用“(*指针对象名)”代替函数名即可,在“(*指针对象名)”之后的括号中根据需要写上实参。例如,语句(*p)(a,b)将调用由p指向的函数,把a和b作为实参传递给函数。圆括号内也可以不含参数,但圆括号必须存在,下列语句的作用是调用无参数的函数:
      (*p)();
      p也需要使用圆括号括起来,以强制运算"*"在被调用前使用。若不加括号,则
      int *p();
就变成了一个返回整数指针的函数声明。
      可以用普通方式使用间接调用的函数返回结果。例如,下列语句将把函数调用返回的结果赋值到i对象。
      i=(*p)(a,b);
      4 函数声明
      必须声明函数的原型。因为现在是用函数名作右值,后面没有括号和参数,编译系统无法判断他们究竟是对象名还是函数名,故要对它们加以声明。声明的形式如下:
      数据类型  函数名(参数的数据类型列表);
      在有规律的多函数调用系统中,这种方式可以大大增强程序的处理能力。
      还可以使用指向函数的函数指针对象作为参数(也就是将函数名作为实参),从而实现函数地址的传递,达到将函数作为参数传给其他函数的目的。下面用一个实例来说明具体的含义。假设已经有分别求两个数的大者、小者及平均值的3个函数max、min和mean。现在,再定义一个函数all如下:
      int all(int x,int y,int (*func)(int ,int))
      {return (*func)(x,y);}
      函数all共有3个形参,其中2个为int形参,1个为指向函数的指针对象func。这个对象的声明为int(*func)(int,int),可用一个定义的函数代替all中的func,例如all(a,b,mean)相当于执行mean(a,b),从而输出a和b的平均值。同理,可用相同方法调用min及max,而all函数的形式一样,只是在调用时改变实参函数名而已。这就增加了函数使用的灵活性,在大型程序设计,尤其是模块设计时特别有用。
      完整的示例程序:
      #include <iostream>
      using namespace std;
      int all(int,int,int (*)(int,int));
      int max(int,int),min(int,int),mean(int,int);      //函数的原型声明
      void main() {
              int a,b;
              cin>>a>>b;
                cout<<"max="<<all(a,b,max)<<endl;
              cout<<"min-"<<all(a,b,min)<<endl;
              cout<<"mean="<<all(a,b,mean)<<endl;
      }
      int all(int x,int y,int (*func)(int,int) {
              return (*func)(x,y);
      }
      int max(int x,int y) {
              return (x>y)?x:y;
      }
      int min(int x,int y) {
              return (x<y)?x:y;
      }
      int mean(int x,int y) {
              return (x+y)/2;
      }
      在本程序中,若输入
      58 62
      则输出
      max=62
      min=58
      mean=60
      另外要注意,函数指针的目标函数已经存在,才能被引用。
2 指向成员的指针
      对象是一个完整的实体。为了支持这一封装,C++包含了指向类成员的指针。可以用普通指针访问内存中给定类型的任何对象,指向类成员的指针来访问某个特定类型的对象中给定类型的任何成员。
      C++既包含指向类数据成员的指针,又包含指向成员函数的指针。C++提供一种特殊的指针类型,其指针指向类的成员,而不是指向该类的一个对象中该成员的一个实例,这种指针成为指向类成员的指针。
      2.1 指向类数据成员的指针
      类并不是对象,但有时可将其视为对象来使用。可以声明并使用指向数据成员的指针或指向对象数据成员的指针。指向对象的指针是比较传统的指针,指向类X中类型为type的数据成员的指针的声明形式为:
      type X :: * pointer;
      若类X的数据成员member的类型type,则语句
      pointer=&x::member;
该成员的地址存入pointer中。注意,取一个类成员的地址使用表达式&X::member,这样得到的地址不是真实地址,而是member在类X的所有对象中的偏移。因此,若要访问某个对象中pointer多指向的成员,应该使特殊的运算符".*"和"->*"。
      指向类数据成员的指针可访问该类的任何对象的公有数据成员。考虑下面类:
      class A {
      public:
              int a,b,c;
      };
      下面的声明表明p是指向类A的整型数据成员的指针:
      int A:: *p;      //指向类A的整形数据成员(a,b,c)的指针
      虽然“::”是作用域分辨符,但在这种情况下,"A ::"最好读成“A的成员”。这时,从内往外看,此声明可读作:p是一个指针,指向类A的数据成员,这些数据是整数。p可以指向仅有的3个数据成员a,b,c中的一个,即类A的唯一一组整形数据成员。不过,p一次只能指向一个数据成员。从这一点看,有点“共同体”类型的味道。p可以使用以下的赋值方式指向A的3个合适的数据成员中的任意一个。
      p=&A :: 数据成员名;
      这时还没有建立任何类A的对象,这是指向类数据成员指针的核心意义。p将访问任何对象的a、b、c成员变量。为了实际使用指向成员的指针,需要使用".*"和"->*"运算符,这是新出现的C++运算符。
      在使用指向数据成员的指针访问对象的某个数据成员时,必须指定一个对象。如果该对象由对象名或引用标识,则使用运算符“.*”;如果是使用指向对象的指针来标识的,则使用运算符“->*”。对数据成员的限制是它们必须是公有的。
      指向类的静态数据成员的指针的定义和使用方法与一般的定义和使用方法一样。
      由于静态数据成员不属于任何对象,所以访问指向静态数据成员的指针时不需要对象。由于静态数据成员是在类中声明的,所以取它的地址时需要进行成员名限定。
      2.2 指向类成员函数的指针
      指向类成员函数的指针与指向类数据成员的指针工作原理相似,用途也相似。只是指向类成员函数的指针的语法更发杂。假设类A的成员函数的指针的语法更复杂。假设类A的成员函数为"void fa(void);",如果要建立一个指针pagn,它可以指向任何无参数和无返回值的类A的成员函数:
      void (A ::*pafn)(void);
      下面的例子说明了pafn如何被赋值并用以调用函数fa:
      pafn=A :: fa;                              //指向类A的成员函数fa的指针pafn
      A x;                                                //类A的对象x
      A *px=&x;                                      //指向类A对象x的指针px
      (x.*pafn)();                                //指向类A的对象x的成员函数fa
      (px->*pafn)();                            //调用类A的对象x的指针px指向的成员函数fa
      指向类X中参数类型列表为list,返回类型为type的成员函数的指针的声明形式为:
      type(X::*pointer)(list);
      如果类X的成员函数fun的原型与pointer所指向的函数的原型一样,则语句
      pointer=X::fun;
将该函数的地址(即它在该类中所有对象中偏移)赋给了指针pointer。与指向类数据成员的指针类似,使用对象名或引用调用pointer所指向的函数时,应使用运算符".*";使用指向对象的指针调用pointer所指向的成员函数时,使用运算符".*";使用指向对象的指针调用pointer所指向的成员函数时,使用运算符"->*"。
      使用指向类成员函数的指针:
      #include <iostream>
      using namespace std;
      class A {
      private:
              int val;
      public:
              A(int i){ val=i; }
              int value(int a) { return val+a;}
      };
      void main() {
              int(A::*pfun)(int);      //声明指向类A的成员函数的指针
              pfun=A::value;              //指针指向具体成员函数value
              A obj(10);                      //创建对对象obj
              cout<<(obj.*pfun)(15)<<endl;//对象使用类的函数指针,输出25
              A *pc=&obj;                                //对象A的指针pc
              cout<<(pc->*pfin)(15)<<endl;//对象指针使用类的函数指针,输出25   
      }
      注意,(obj.* pfun)或(pc->*pfun)必须使用括号括起来。
      在派生类中,当一个指向基类成员函数的指针指向一个虚函数,并且通过指向对象的基类指针(或引用)访问这个虚函数时,仍会产生多态性。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值