类成员函数指针详解

C++指向函数的指针定义方式为:

返回类型 (*指针名)(函数参数列表),例如 void (*p)(int)是指向一个返回值为void 参数为int类型的函数。

而若想定义一个指向类成员函数的函数指针该怎么定义呢?对成员函数指针的使用。

(1)非静态成员函数

定义方式:返回类型 (类名::*指针名)(函数参数列表)例如void (A::*p)(int)是一个指向A类中成员函数的函数指针。

赋值方式:p=&A::函数名,而一般的函数指针的赋值是p=函数名即可,注意区别。(成员函数必须是public类型的)

调用方式:成员函数指针的调用必须通过类对象进行调用,a.*p(int)即可调用成员函数(该成员函数是public类型的)

(2)静态成员函数

对于静态成员函数的定义和使用方法都与普通函数指针的相同,只是在赋值的时候和非静态成员们函数指针的赋值方法相同。

因为静态成员函数的指针类型是与普通函数指针类型相同的。


小结:

看起来C++的设计者对标点符号有着由衷的感情!C++相对于C增加了三种特殊运算符来支持成员指 针。"::*"用于指针的声明,而"->*"和".*"用来调用指针指向的函数。这样看起来对一个语言模糊而又很少使用的部分的过分关注是多余 的。(你当然可以重载"->*"这些运算符,但这不是本文所要涉及的范围。)


类的成员函数指针的使用 :

//A类与B类的定义
class A
{
public:
    void Test()
    {
        cout << "A::Test()" << endl;
    }
};

class B : public A
{
public:
    void Test()
    {
        cout << "B::Test()" << endl;
    }
};

//定义类的成员函数指针
typedef void (A::*A_mFun)(void);
typedef void (B::*B_mFun)(void);

//Code
A a;
B b;
A_mFun pAFun = &(A::Test);  //Note:Test成员函数必须为public,否则会报错
B_mFun pBFun = &(B::Test);  //Note:Test成员函数必须为public,否则会报错
(a.*pAFun)();               //输出A::Test()
(b.*pBFun)();               //输出B::Test()
(b.*pAFun)();               //输出A::Test()

A* pA = &a;
B* pB = &b;
(pA->*pAFun)();             //输出A::Test()
(pB->*pBFun)();             //输出B::Test()
(pB->*pAFun)();             //输出A::Test(),B*变量可赋值给A*变量

//******Error******
(pA->*pBFun)();            //Error: cannot convert from 'A *' to 'B *'
                           //Error:  cannot dereference a 'B_mFun' on a 'A *'
//*****************

//******OK******
B* pB_new = (B*)pA;        //强转但本质上是有缺失的     
(pB_new->*pBFun)();        //输出B::Test(),虽OK但切记勿这样使用,此处仅当实验演示
//本质上类的成员函数就是带this指针的函数,但在使用时编译器上会增加一些限制,
//比如类的成员函数指针不能和一般的变量指针及一般的函数指针变量之间互相强转等,这在前面随笔中有提到
//**************
//以下才是真正有意义的,在MFC的框架机制中运用较多
//用基类变量指针+基类成员函数指针调用子类的函数
A_mFun pAFun_B = (A_mFun)&(B::Test);
A* pA_B = &b;
(pA_B->*pAFun_B)();          //输出B::Test()

//例如:
//对话框的消息映射有:
ON_BN_CLICKED(IDC_BTN_NEW_DLG, &CTestDlg::OnBnClickedBtnNewDlg)
//将ID为IDC_BTN_NEW_DLG的按钮的响应事件映射为CTestDlg的成员函数OnBnClickedBtnNewDlg
//我们现在来详细研究下ON_BN_CLICKED
#define ON_BN_CLICKED(id, memberFxn) \
    ON_CONTROL(BN_CLICKED, id, memberFxn)
#define ON_CONTROL(wNotifyCode, id, memberFxn) \
{ WM_COMMAND, (WORD)wNotifyCode, (WORD)id, (WORD)id, AfxSigCmd_v, \
    (static_cast< AFX_PMSG > (memberFxn)) },
typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);
//可以看到这里将子类成员函数的地址赋给了基类成员函数指针类型的变量


对于单类型的成员函数指针的使用就是上述的内容,但是对于C++来说,继承是其三大特性之一,那么对于继承类来说,成员函数指针怎么实现动态呢?

(1)赋值问题
与正常的派生类指针或引用可以赋于基类指针或引用不同,基类成员函数可以赋于派生类成员函数指针(任何情况下都不会出错),反之派生类成员函数在未覆盖基类函数名的情况下也能赋于基类成员函数指针。如下例:
class A                                               class B                                            class C:public A,B
{                                                       {                                                     {
public:                                                public:                                         public:
void printA(int)                                   virtual void printB(int)               void printB(int)
{                                                          {                                                   {
}                                                           }                                                   }
};                                                         };                                                };

void (A::*Pa)(int);void (B::*Pb)(int);void (C::*Pc)(int)
pa=&C::printA;(正确)相当于将C中A类对象的函数传给指针。
pb=&C::printB;(错误)因为printB已经在C类中修改,两者的类型不同
pc=&A::printA;(正确)
pc=&B::printB;(正确)
总结:只要不被派生类覆盖的函数均可以赋给基类成员函数指针。

(2)多态调用
接上例:
pb=&B::printB;
(b.*pb)(i);(调用b类中的printB)
(c.*pb)(i);(调用c类中的printB)
但是对于pc指针只能由c类对象调用,不能由基类调用。
根据对象实例的类型去判断应该调用的函数。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值