类成员函数指针

類別成員函式指標（member function pointer），是C++語言的一類指標資料類型，用於儲存一個指定類具有給定的形參列表與返回值類型的成員函式的存取資訊。

語法

使用::*聲明一個成員指標類型，或者定義一個成員指標變數。使用.*或者->*呼叫類別成員函式指標所指向的函式，這時必須繫結（binding）於成員指標所屬類的一個例項的位址。例如：
struct X {
  void f(int){ };
  int a;};void (X::* pmf)(int); //一个类成员函数指针变量pmf的定义pmf = &X::f;            //类成员函数指针变量pmf被赋值X ins, *p;p=&ins;(ins.*pmf)(101);       //对实例ins，调用成员函数指针变量pmf所指的函数(p->*pmf)(102);      //对p所指的实例，调用成员函数指针变量pmf所指的函数
由於C++運算子優先級列表中，函式呼叫運算子()的優先級高於.*與->*，因此成員函式指標所指的函式被呼叫時，必須把例項物件或例項指標、.*或->*運算子、成員函式指標用括弧括起來，如上例所示。
C++標準規定，非靜態成員函式不是左值，因此非靜態成員函式不存在運算式中從函式左值到指標右值的隱式轉換，非靜態成員函式指標必須通過&運算子顯式獲得。所以上例中，pmf = X::f; 將編譯報錯。

類別成員函式指標的用途

類別成員函式指標的主要用途是把資料與相關代碼結合在一起。這與委託（delegate）、函子（functor）、閉包（closure）等概念很像。雖然C++對此支援的並不太好。
MFC類體系中，Windows訊息傳遞處理機制是基於CCmdTarget類及其衍生類別的靜態成員變數與靜態成員函式GetThisMessageMap()。用戶所寫的類中的Windows訊息處理常式（例如OnCommand）必須轉換為CCmdTarget::*的成員函式指標類型AFX_PMSG，儲存在該用戶類的_messageEntries靜態陣列中。
typedef void (CCmdTarget::*AFX_PMSG)(void);
呼叫用戶類中該訊息處理常式時，根據該函式儲存在_messageEntries中的signature（一個無符號整型表示的函式的形參類型列表與返回值類型），把類型為void (CCmdTarget::*AFX_PMSG)(void)的成員函式指標強制轉為其它類型的CCmdTarget成員函式指標（例如void (AFX_MSG_CALL CWnd::*pfn_v_i_i)(int, int)，目前在union MessageMapFunctions中列出了近百種CCmdTarget成員函式指標），然後呼叫轉換後的成員函式指標。這是基於Visual C++編譯器把單繼承的成員函式指標編譯為只儲存了函式的記憶體起始位址，因此可以在同一個單繼承類中把一種類型的成員函式指標強制轉換為另一種成員函式指標，或者把單繼承衍生類別的成員函式指標強制轉換為基礎類別成員函式指標。這是打破了C++標準的違例辦法。例如，對於CWnd::OnCommand函式，轉換過程是：
BOOL (CWnd::*)(WPARAM, LPARAM lParam) => void (CWnd::*)() => void (CCmdTarget::*)()

例子

#include <iostream>
 
class Test; //一个未定义的类。class Test2 
{
       int i;public:
void foo(){ }};class Test3{
int i;public:
        void foo(){ }};
 

class Test4:public Test2 , public Test3 //多继承的类 {
int i;public:
         void foo(  ) { }};class Test5:virtual public Test4 //虚继承的类 {
int i;public:
         void foo(  ) { }};int main(){ 
std::cout <<"Test3类成员函数指针长度="<<sizeof(void(Test3::*)()) <<'\n';std::cout <<"Test4类成员函数指针长度="<<sizeof(void(Test4::*)()) <<'\n';std::cout <<"Test5类成员函数指针长度="<<sizeof(void(Test5::*)()) <<'\n'; 
std::cout <<"Test类成员函数指针长度="<<sizeof(void(Test::*)()) <<'\n';//以下可以打开IDE的反汇编（Disassembly）窗口观察成员函数指针的赋值与调用Test5 a;                                                             //定义一个实例void (Test5::* pfunc)()=&Test5::foo;                //定义类成员函数指针并赋值pfunc=&Test5::Test2::foo;pfunc=&Test2::foo;pfunc=&Test5::Test3::foo;(a.*pfunc)();  //调用类成员函数指针，同时使用了虚基表（vbtbl）索引值与this指针调整值}

未知繼承的成員函式指標例子

使用Microsoft Visual C++編譯32位元程式：
//main.cpp 不需要任何特殊的编译选项class Test;                                         //一个forward declaration、未定义的类
 
typedef void(Test::*NULLFUNCPTR)(); //未知继承的类成员函数指针的类型定义extern Test  var;                                 //外部定义的全局对象extern  NULLFUNCPTR pfunc;           //外部定义的类成员函数指针的变量void set();                                      //外部定义的对类成员函数指针pfunc初始化void Helper(Test &var, NULLFUNCPTR pf){
(var.*pf)();}int main(){ 
size_t ss=sizeof(NULLFUNCPTR);
        set();  
Helper(var, pfunc  );}
// DefineTest.cpp //必须用Visual C++编译选项/vmgclass t2 
{
    int i;public:
    void virtual foo(){ }};class t3{
     int i;public:
     t3() { i=101;}
     void virtual foo()
     { 
  printf("In t3::foo()  %d\n",i);
     }
    virtual void foo3()
    { 
       printf("In t3::foo3()  %d\n",i);
     }};class t4:public t2 , public t3 //多继承的类 {
    int i;public:
    void virtual foo(  ) { }};class Test:virtual public t4 //虚继承的类 {
    int i;public:
    Test()  {i=102;}
     void virtual foo (  ) 
     { 
           printf("In Test::foo()  %d\n",i);
     }
     virtual void bar() { } 
};/* 类成员函数指针的类型定义。因为使用了/vmg编译选项，该类Test的成员函数指针类型为16字节长。如果不使用/vmg编译选项，在本文件中有类Test的完整定义，所以编译器会把类Test的成员函数指针类型按照虚继承的情形定义为12字节长，这导致与main.cpp的类Test的未知继承的成员函数指针类型（16字节长）不匹配，程序运行时出错  */typedef void(Test::*NULLFUNCPTR)(); 


Test var;                             //全局对象，用于main.cppNULLFUNCPTR pfunc;       //全局成员函数指针，用于main.cpp

void set() //初始化pfunc{
size_t ss=sizeof(NULLFUNCPTR); //长度是16；如果不用/vmg编译选项则为12/* 赋初值。pfunc的16字节依次存入了  字节0-3：t3::foo3的函数体开始地址；  字节4-7：值8（表示多继承情形下从Test::t4到Test::t3的偏移量）；  字节8-11：值4（表示从Test实例的首地址到Test的虚基类表指针vbptr的偏移值vtordisp；             如果没有定义Test::bar，则Test没有自己的虚函数表，此偏移值为值0.              即vbptr在类对象的开始地址可能为0或4，             所以对于未知继承情形，必须保存虚基类表指针vbptr相对于对象首地址的偏移值）；  字节12-15：值4（表示在虚基类表vbtbl中，              保存了从虚基类Test::t4到Test::vbptr的地址偏移量的条目的字节位置）。  Visual C++编译器对多重继承且虚继承的对象的基类部分的地址的计算表达式：       this+ *( *(this+虚基表指针的地址调整值) + 虚基表中条目的字节位置) + 多重继承的地址调整值 */
pfunc=&t3::foo3;   

/* 上述语句右端是&t3::foo或者&Test::t3::foo，pfunc实际对应到了Test::t3的虚表的第一项所保存的函数，即Test::foo()的thunk（用于在调用Test::foo()之前，把this指针从指向Test::t3调整到指向Test实例的开始地址） */}