C++相对于C增加了三种特殊运算符来支持成员指针。::* 用于指针的声明,而 ->* 和 .* 用来调用指针指向的函数。另外,标准C++中还有一条有趣的规则是:你可以在类定义之前声明它的成员函数指针类型。

同类型下,成员函数指针简单的使用:

#include <iostream>
using namespace std;
 
class test;
typedef void (test::*FunType)();
 
class test
{
public:
    void print() { cout << "hello" << endl; }
};
 
int main()
{
    FunType pfun = &test::print;
    test t;
 
    (t .* pfun)();           // 输出 hello
 
    ( (&t) ->* pfun )();     // 输出 hello
 
    return 0;
}

继承体系下,成员函数的指针的使用

考虑如下代码:

class SomeClass;
typedef void (SomeClass::* SomeClassFunction)(void);
 
void Invoke(SomeClass *pClass, SomeClassFunction funcptr)
{
    (pClass ->* funcptr)();
};

下面主要介绍继承体系下的,成员函数指针转化及调用中的事项:

  1. 成员函数指针类型转化,依赖于static_cast。
  2. 一般常用的方式是,派生类指针类型转化为基类指针类型,因为派生类对象指针转化为基类对象的指针是直接的。

派生类成员函数指针对应的函数不是虚函数

派生类可以直接使用转化为基类的函数指针,或者对象可以转化为基类调用。

#include <iostream>
using namespace std;
 
struct Base {};
 
struct Derived : public Base
{
    void print() { cout << "hello" << endl; }
};
 
typedef void (Base::*Base_Fun)();
 
int main()
{
    Derived obj;
    Base_Fun pFun = static_cast< Base_Fun >( &Derived::print );
    (obj .* pFun)();             // 派生类自己调用
    ((Base*)(&obj) ->* pFun)(); // 基类调用
 
    return 0;
}

基类中可以用this指针调用派生类的成员函数指针

#include <iostream>
#include <map>
#include <string>
using namespace std;
 
template<typename T>
struct CBusMsgHandler
{
    typedef void (CBusMsgHandler::*Fun_Type)();
 
    CBusMsgHandler()
    {
       T & t = static_cast<T &>( *this );
       t.__RegistAll_In_Map();
    }
 
    virtual void AddRegisterMessage(string const& s, Fun_Type fun)
    {
       m_[ s ] = fun;
    }
 
    void OnMsg( string const& s )
    {
       map<string, Fun_Type>::iterator it = m_.find( s );
       if ( it != m_.end() )
       {
           (this ->* it->second)(); // 调用时,使用this指针
       }
    }
 
private:
    map<string, Fun_Type> m_;
};
 
struct derived : public CBusMsgHandler< derived >
{
    void __RegistAll_In_Map()
    {
       AddRegisterMessage( "hello", static_cast<CBusMsgHandler< derived >::Fun_Type>(&derived::print_hello) );
       AddRegisterMessage( "world", static_cast<CBusMsgHandler< derived >::Fun_Type>(&derived::print_world) );
    }
 
    void print_hello() { cout << "hello" << endl; }
    void print_world() { cout << "world" << endl; }
};
 
int main()
{
    derived data;
    data.OnMsg("hello");
 
    return 0;
}

这个例子的基本内容是:基类中存储派生类的成员函数指针(转换),当外面条件触发到基类的回调时,基类中可以使用this指针来调用存储的派生类的成员函数指针,但是效果是派生类对象的成员函数被调用,这也是消息总线的核心。

派生类成员函数指针对应的函数是虚函数

派生类成员函数指针对应的函数是虚函数时,它必须是基类对应函数的覆盖,即基类必须有这个虚函数的声明。否则,后果是运行时程序崩溃。

#include <iostream>
using namespace std;
 
struct Base
{
    virtual void print() { cout << "world" << endl; }
};
 
struct Derived : public Base
{
    void sing() { cout << "hello world" << endl; }
    virtual void print() { cout << "hello" << endl; }
};
 
typedef void (Base::*Base_Fun)();
 
int main()
{
    Derived obj;
    Base_Fun pFun = static_cast< Base_Fun >( &Derived::print );
    ((Base*)(&obj) ->* pFun)(); // 派生类重定义,输出 hello
 
    pFun = static_cast< Base_Fun >( &Derived::sing );
    (obj .* pFun)();  // 可以混合使用,输出 hello world
 
    return 0;
}