C++中,成员指针是最为复杂的语法结构。但在事件驱动和多线程应用中被广泛用于调用回叫函数。在多线程应用中,每个线程都通过指向成员函数的指针来调用该函数。在这样的应用中,如果不用成员指针,编程是非常困难的。

  刚遇到这种语法时也许会让你止步不前。但你会发现,使用恰当的类型定义之后,复杂的语法是可以简化的。本文引导你了解成员函数指针的声明,赋值和调用回叫函数。

  成员函数指针的声明

  一个成员函数指针包括成员函数的返回类型,后随::操作符类名,指针名和函数的参数。初看上去,语法有点复杂。其实可以把它理解为一个指向原函数的指针,格式是:函数返回类型,类名,::操作符,指针星号,指针名,函数参数。

  一个指向外部函数的指针声明为:

 void (*pf)(char *, const char *); 

 void strcpy(char * dest, const char * source); 

 pf=strcpy; 

  一个指向类A成员函数的指针声明为:

 void (A::*pmf)(char *, const char *); 

  声明的解释是:pmf是一个指向A成员函数的指针,返回无类型值,函数带有二个参数,参数的类型分别是char * 和 const char *。除了在星号前增加A:: ,与声明外部函数指针的方法一样。

  赋值

  给成员指针赋值的方法是将函数名通过指针符号&赋予指针名。如下所示:

class A
{

  public:

   void strcpy(char *, const char *);

   void strcat(char *, const char *);

};
pmf = &A::strcpy; 

  有些老的编译器可以通过没有&号的赋值方式,但标准C++强制要求加上&号。

  使用类型定义 
  

  C++新标准规定了一种新的方法,使用别名声明来定义类型的别名:

  using SI=Sales_item; //SI是Sales_item的同义词

  

  这种使用关键字using作为别名声明的开始,其后紧跟别名和等号,其作用是把等号左侧的名字规定成等号右侧类型的别名。

 

  类型别名和类型的名字等价,只要是类型的名字能出现的地方,就能使用类型别名。

  

  eg: SI item;  //等价于Sales_item item;

  可以用类型定义来隐藏复杂的成员指针语法。例如,下面的语句定义了PMA是一个指向A成员函数的指针,函数返回无类型值,函数参数类型为char * 和 const char *:

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

  

  using PMA=void (A::*)(char *,const char *);

  PMA pmf= &A::strcat; // pmf是PMF类型(类A成员指针)的变量

  下文会看到使用类型定义特别有利于声明成员指针数组。

  通过成员指针调用成员函数

可以在不必知道函数名的情况下,通过成员指针调用对象的成员函数。例如,函数dispatcher有一个变量pmf,通过它调用类成员函数,不管它调用 的是strcpy()函数还是strcat()函数。指向外部原函数的指针和指向类成员函数的指针是有很大区别的。后者必须指向被调函数的宿主对象。因 此,除了要有成员指针外,还要有合法对象或对象指针。

  现举例做进一步说明。假设A有二个实例,成员函数指针支持多态性。这样在成员指针调用虚成员函数时是动态处理的(即所谓后联编 - 译注)。注意,不可调用构造和析构函数。示例如下:

    A a1, a2;

  A *p= &a1; //创建指向A的指针

  //创建指向成员的指针并初始化

  void (A::*pmf)(char *, const char *) = &A::strcpy;

  //要将成员函数绑定到pmf,必须定义呼叫的对象。

  //可以用*号引导:

  void dispatcher(A a, void (A::*pmf)(char *, const char *))

  {

   char str[4];

   (a.*pmf)(str, “abc”); //将成员函数绑定到pmf

  }

  //或用A的指针表达方式指向成员指针:

  void dispatcher(A * p, void (A::*pmf)(char *, const char *))

  {

   char str[4]; (p->*pmf)(str, “abc”);

  }

  //函数的调用方法为:

  dispatcher(a, pmf); // .* 方式

  dispatcher(&a, pmf); // ->* 方式

高级使用技巧

  以上是成员函数的基本知识。现在介绍它的高级使用技巧。

  成员指针数组

  在下例,声明了一个含有二个成员指针的数组,并分配类的成员函数地址给成员指针:

  PMA pmf[2]= {&A::strcpy, &A::strcat}; 
也就是
      void (A::*PMA[2])(char *, const char *)= {&A::strcpy, &A::strcat};

  这样的数组在菜单驱动应用中很有用。选择菜单项后,应用将调用相应的回叫函数,如下所示:

    enum MENU_OPTIONS { COPY, CONCAT };

  int main()
  {
   MENU_OPTIONS option; char str[4];
   //从外部资源读取选项
   switch (option)
   {
    case COPY:

     (pa->*pmf[COPY])(str, “abc”);

     break;

    case CONCAT:

     (pa->*pmf[CONCAT])(str, “abc”);

     break;

     //…

   }
  } 

  Const 类型的成员函数

  成员指针的类型应该与成员函数类型一致。上面例子中的pmf 可以指向A的任意函数,只要该函数不是const类型。如下所示,如果将touppercase()的地址分配给pmf,将导致编译出错,因为touppercase() 的类型是const。

   Class A
  {

   public:

     void strpcy(char *, const char *);

     void strcat(char *, const char *);

     void touppercase(char *, const char*) const;

  };

  pmf=&A::touppercase; //出错,类型不匹配

  //解决的方法是声明一个const类型的成员指针:

  void (A::pcmf)(char *, const char *) const;

  pcmf=&A::touppercase; // 现在可以了

  有些差劲的编译器允许一个非const类型的成员指针指向const类型的成员函数。这在标准C++是不允许的。

C++成员函数指针的应用的更多相关文章

  1. 为什么 C++ 中成员函数指针是 16 字节?

    当我们讨论指针时,通常假设它是一种可以用 void * 指针来表示的东西,在 x86_64 平台下是 8 个字节大小.例如,下面是来自 维基百科中关于 x86_64 的文章 的摘录: Pushes a ...

  2. C++ 指向成员函数指针问题

    成员函数指针与常规指针不同,一个指向成员变量的指针并不指向一个内存位置.通常最清晰的做法是将指向数据成员的指针看作为一个偏移量. class ru_m { public: typedef int (r ...

  3. [Reprint]C++普通函数指针与成员函数指针实例解析

    这篇文章主要介绍了C++普通函数指针与成员函数指针,很重要的知识点,需要的朋友可以参考下   C++的函数指针(function pointer)是通过指向函数的指针间接调用函数.相信很多人对指向一般 ...

  4. 类成员函数指针 ->*语法剖析

    在cocos2d-x中,经常会出现这样的调用,如 ->*,这个是什么意思呢,如下面得这个例子: , 其实这是对类的成员函数指针的调用,在cocos2dx中,这种形式多用于回调函数的调用.如我们经 ...

  5. C++ 类的成员函数指针 ( function/bind )

    这个概念主要用在C++中去实现"委托"的特性. 但现在C++11 中有了 更好用的function/bind 功能.但对于类的成员函数指针的概念我们还是应该掌握的. 类函数指针 就 ...

  6. C++的类成员和类成员函数指针

    类成员函数指针: 用于访问类成员函数,和一般函数指针有区别. 类成员函数处理的是类数据成员,声明类成员函数指针的同时,还要指出具体是哪个类的函数指针才可以.调用时也要通过对象调用. 而对于类的静态成员 ...

  7. 从汇编看c++成员函数指针(三)

    前面的从汇编看c++中成员函数指针(一)和从汇编看c++成员函数指针(二)讨论的要么是单一类,要么是普通的多重继承,没有讨论虚拟继承,下面就来看一看,当引入虚拟继承之后,成员函数指针会有什么变化. 下 ...

  8. 从汇编看c++成员函数指针(二)

    下面先看一段c++源码: #include <cstdio> using namespace std; class X { public: virtual int get1() { ; } ...

  9. 从汇编看c++中成员函数指针(一)

    下面先来看c++的源码: #include <cstdio> using namespace std; class X { public: int get1() { ; } virtual ...

  10. VB6/VBA中跟踪鼠标移出窗体控件事件(类模块成员函数指针CHooker类应用)

    一.关于起因 前几天发了一篇博文,是关于获取VB类模块成员函数指针的内容(http://www.cnblogs.com/alexywt/p/5880993.html):今天我就发一下我的应用实例. V ...

随机推荐

  1. Visualizing CNN Layer in Keras

    CNN 权重可视化 How convolutional neural networks see the world An exploration of convnet filters with Ker ...

  2. Android 应用安装

    DDMS下Files Explorer /data/app/xxx.apk 安装过程:1.拷贝文件xxx.apk到/data/app/xxx-1.apk 2.在/data/data目录下创建一个文件夹 ...

  3. ew代理实战

    前言 渗透内网代理必不可少,本文做个记录 正文 工具下载地址 http://rootkiter.com/EarthWorm/ ssocksd开启 socks5 代理 环境 代理:192.168.211 ...

  4. 关于Redo Log的基本操作

    1.创建新的日志组 alter database add logfile group 4 ('/u01/oracle/product/10.0.2/oradata/ORCL/redo41.log') ...

  5. 禅道Bug管理工具环境搭建

    下载地址:http://sourceforge.net/projects/zentao/files/8.2/ZenTaoPMS.8.2.stable.exe/download 1.解压ZenTaoPM ...

  6. Mac下驱动BCM20702A0 USB蓝牙

    偶然高了一个USB蓝牙,查到Mac下能识别,无法驱动,就去找了一下.方法很简单,就是把蓝牙的PID和VID加入到驱动里边去,具体方法和步骤如下: 1. 进入到/System/Libary/Extens ...

  7. [VS2008] [.NET 3.5] 如何解决 The imported project "C:\Windows\Microsoft.NET\Framework\v3.5\Microsoft.CompactFramework.CSharp.targets" was not found

    重新安装或者修复 NETCFv35PowerToys https://download.microsoft.com/download/f/a/c/fac1342d-044d-4d88-ae97-d27 ...

  8. 转:asp.net 中的viewstate

    概述 ViewState是一个被误解很深的动物了.我希望通过此文章来澄清人们对ViewState的一些错误认识.为了达到这个目的,我决定从头到尾详细的描述一下整个ViewState的工作机制,其中我会 ...

  9. 【原创】使用Java进行Clob转String字符串

    背景 oracle字段类型varchar2最大长度4000,超过的怎么办 解决 使用clob类型,大字段,无长度限制 问题 使用Java的JDBC读取的RS获取的clob类型无法正常使用,封装的rs. ...

  10. win中使用cmd杀端口

    最近在win开发时,总是遇到端口占用的情况...可能是跑的程序太多了吧 当你测试一个demo时遇到这个就很恶心.. 记一下 netstat -ano | findstr 80 //列出进程极其占用的端 ...