函数指针以及成员函数指针2

2、成员函数指针

在C++中,普通的函数指针不能指向类中的成员函数,必须使用成员函数指针。

(1) 成员函数指针声明如下:

float (SomeClass::*my_memfunc_ptr)(int, char *);
// 常成员函数
float (SomeClass::*my_const_memfunc_ptr)(int, char *) const;

注:1)以上使用了特殊操作符:( ::*),以及SomeClass是声明的一部分。2)成员函数指针只能指向该类的成员函数。3)对于一个类中属于常成员,但由不同参数组合的函数指针是不同类型的函数指针。对于4种不同地调用约定( __cdecl , __stdcall __fastcall , and  __thiscall),函数指针类型也是不同地。4)为了避免混淆,也应当使用typedef来声明成员函数指针。

(2)使用声明的函数指针指向成员函数float SomeClass::some_member_func(intchar *)

my_memfunc_ptr = &SomeClass::some_member_func;
 // 操作符语法表示:
 my_memfunc_ptr = &SomeClass::operator !;
 // 不允许取构造函数和析构函数的地址

(3)调用成员函数指针,首先需要提供一个SomeClass的实例,以及必须使用特殊操作符: ->*,这个操作符优先级低,故需要一个括号。

 SomeClass *x = new SomeClass;
  (x->*my_memfunc_ptr)(6, "Another Arbitrary Parameter");
// 如果在栈中实例化对象,则使用.*操作符
  SomeClass y;
  (y.*my_memfunc_ptr)(15, "Different parameters this time");

(4)注意项:

1)成员函数指针可以被赋值0

2)对于同一个类中的成员函数指针,操作符==以及!=适用。成员函数指针可以和0比较以确定是否是一个空指针。

3)不像普通函数指针,成员函数指针不适用“不等操作符”(<><=>=)。它也可以作为非类型模板参数,但较少编译器支持。

4)成员函数指针不能指向静态成员函数,必须使用普通的函数指针指向静态成员函数,故应当称为:非静态成员函数指针。

(5)成员函数指针在派生关系的类之间应用奇怪点

父类的成员函数指针不能指向重载父类虚函数的子类函数。如下:

class SomeClass {
 public: 
    virtual void some_member_func(int x, char *p) {
       printf("In SomeClass"); };
};

class DerivedClass : public SomeClass {
 public:
 // 如果取消注释下一行,在 line (*)的代码会失败
//    virtual void some_member_func(int x, char *p) { printf("In DerivedClass"); };
};

int main() {
    // 声明一个类SomeClass成员函数指针
    typedef void (SomeClass::*SomeClassMFP)(int, char *);
    SomeClassMFP my_memfunc_ptr;
    my_memfunc_ptr = &DerivedClass::some_member_func; // ---- line (*)
}
&DerivedClass::some_member_func 是类SomeClass的成员函数指针,不是类DerivedClass的成员。如果类DerivedClass重载some_member_func函数,代码将不能编译,因为&DerivedClass::some_member_func将会变成类DerivedClass的成员函数指针。

(6)不同类之间的成员函数指针互相转换非常模糊,因为标准和编译器之间相差很大。这里不详细说明。

(7)成员函数指针的用途

成员函数指针可能有点奇怪,而且用途也很窄,主要有两方面:1)为C++初学者展示C++语法而写的不自然例子。2)实现委托(delegates)。

1)成员函数指针也在STL和boost库中的one-line函数适配器中有简单的使用,允许使用成员函数和标准算法,在这种情况下,成员函数指针只出现在编译时期,在编译后的代码中实际不存在函数指针。

2)成员函数指针最有趣的应用其实是定义复杂接口,目前为止,成员函数指针最让人所知的应用是各种应用框架。如:MFC框架的消息系统核心。当使用MFC的消息映射宏(如ON_COMMAND)时,实际使用的是一个包含消息IDs和成员函数指针(特别是CCmdTarget::*成员函数指针)的数组。这是处理消息的MFC类必须继承CCmdTarget类的原因,但是各种消息处理函数有不同的参数列表,因此数组必须包含各种类型的成员函数指针。MFC设计者使用了一个令人讨厌的hack,将所有可能的成员函数指针放进一个联合union中,以避免C++类型检查。

(8)成员函数指针的一个关键点(05年结论,c++11标准还有待验证)

C++标准允许成员函数指针之间的转换,但转换后,却不允许你调用转换后的指针,是不是很荒谬。说荒谬有三个理由:1)在许多流行的编译器中转换不总是能成功(这种转换符合标准,却不兼容);2)若转换成功,调用转换后的函数指针的行为与你期望的一样,并不会出现"未定义行为"的错误。(调用是兼容的,可移植的,但不符合标准!)3)允许转换却不允许调用是完全没用的。若转换和调用都是可行的,则能够很容易实现高效的委托,将给C++带来巨大的益处。为了使得这个有争议的论断可信,考虑下面的标准代码:

class SomeClass;

typedef void (SomeClass::* SomeClassFunction)(void);

void Invoke(SomeClass *pClass, SomeClassFunction funcptr) {
  (pClass->*funcptr)(); };
注意编译器必须在 类SomeClass没有定义地情况下生成调用 成员函数指针的汇编代码,显然,除非链接器做一些极端复杂的优化,否则在类没有实际定义的情况下运行这段代码是不现实的( 这部分代码  必须  在不知道实际的类定义的情况下正确的运行)。这直接证明了可以安全的调用一个从完全不同的类转换过来的成员函数指针。

要解释这个点的另外一半(即成员函数指针的转换不像标准说的那样运行),需要讨论编译器实现函数指针的细节,这也会帮助理解为什么成员函数指针在使用上有那么多限制,通过普通的错误消息来获得成员函数指针的准确描述是很困难的,所以作者查了很多编译器生成的汇编代码。

未完待续。。。。



你可能感兴趣的:(C++,成员函数指针,C++)