成员函数指针与高性能的C 委托(上篇) - 周翔的Blog - CSDNBlog

来源:百度文库 编辑:神马文学网 时间:2024/04/29 02:00:32
 成员函数指针与高性能的C++委托(上篇)
成员函数指针与高性能的C++委托(上篇)
Member Function Pointers and the Fastest Possible C++ Delegates
撰文:Don Clugston
翻译:周翔
引子
标准C++中没有真正的面向对象的函数指针。这一点对C++来说是不幸的,因为面向对象的指针(也叫做“闭包(closure)”或“委托(delegate)”)在一些语言中已经证明了它宝贵的价值。在Delphi (Object Pascal)中,面向对象的函数指针是Borland可视化组建库(VCL,Visual Component Library)的基础。而在目前,C#使“委托”的概念日趋流行,这也正显示出C#这种语言的成功。在很多应用程序中,“委托”简化了松耦合对象的设计模式[GoF]。这种特性无疑在标准C++中也会产生很大的作用。
很遗憾,C++中没有“委托”,它只提供了成员函数指针(member function pointers)。很多程序员从没有用过函数指针,这是有特定的原因的。因为函数指针自身有很多奇怪的语法规则(比如“->*”和“.*”操作符),而且很难找到它们的准确含义,并且你会找到更好的办法以避免使用函数指针。更具有讽刺意味的是:事实上,编译器的编写者如果实现“委托”的话会比他费劲地实现成员函数指针要容易地多!
在这篇文章中,我要揭开成员函数指针那“神秘的盖子”。在扼要地重述成员函数指针的语法和特性之后,我会向读者解释成员函数指针在一些常用的编译器中是怎样实现的,然后我会向大家展示编译器怎样有效地实现“委托”。最后我会利用这些精深的知识向你展示在C++编译器上实现优化而可靠的“委托”的技术。比如,在Visual C++(6.0, .NET, and .NET 2003)中对单一目标委托(single-target delegate)的调用,编译器仅仅生成两行汇编代码!
函数指针
下面我们复习一下函数指针。在C和C++语言中,一个命名为my_func_ptr的函数指针指向一个以一个int和一个char*为参数的函数,这个函数返回一个浮点值,声明如下:
float (*my_func_ptr)(int, char *);
//为了便于理解,我强烈推荐你使用typedef关键字。
//如果不这样的话,当函数指针作为一个函数的参数传递的时候,
// 程序会变得晦涩难懂。
// 这样的话,声明应如下所示:
typedef float (*MyFuncPtrType)(int, char *);
MyFuncPtrType my_func_ptr;
应注意,对每一个函数的参数组合,函数指针的类型应该是不同的。在Microsoft Visual C++(以下称MSVC)中,对三种不同的调用方式有不同的类型:__cdecl, __stdcall, 和__fastcall。如果你的函数指针指向一个型如float some_func(int, char *)的函数,这样做就可以了:
my_func_ptr = some_func;
当你想调用它所指向的函数时,你可以这样写:
(*my_func_ptr)(7, "Arbitrary String");
你可以将一种类型的函数指针转换成另一种函数指针类型,但你不可以将一个函数指针指向一个void *型的数据指针。其他的转换操作就不用详叙了。一个函数指针可以被设置为0来表明它是一个空指针。所有的比较运算符(==, !=, <, >, <=, >=)都可以使用,可以使用“==0”或通过一个显式的布尔转换来测试指针是否为空(null)。
在C语言中,函数指针通常用来像qsort一样将函数作为参数,或者作为Windows系统函数的回调函数等等。函数指针还有很多其他的应用。函数指针的实现很简单:它们只是“代码指针(code pointer)”,它们体现在汇编语言中是用来保存子程序代码的首地址。而这种函数指针的存在只是为了保证使用了正确的调用规范。
成员函数指针
在C++程序中,很多函数是成员函数,即这些函数是某个类中的一部分。你不可以像一个普通的函数指针那样指向一个成员函数,正确的做法应该是,你必须使用一个成员函数指针。一个成员函数的指针指向类中的一个成员函数,并和以前有相同的参数,声明如下:
float (SomeClass::*my_memfunc_ptr)(int, char *);
//对于使用const关键字修饰的成员函数,声明如下:
float (SomeClass::*my_const_memfunc_ptr)(int, char *) const;
注意使用了特殊的运算符(::*),而“SomeClass”是声明中的一部分。成员函数指针有一个可怕的限制:它们只能指向一个特定的类中的成员函数。对每一种参数的组合,需要有不同的成员函数指针类型,而且对每种使用const修饰的函数和不同类中的函数,也要有不同的函数指针类型。在MSVC中,对下面这四种调用方式都有一种不同的调用类型:__cdecl, __stdcall, __fastcall, 和 __thiscall。(__thiscall是缺省的方式,有趣的是,在任何官方文档中从没有对__thiscall关键字的详细描述,但是它经常在错误信息中出现。如果你显式地使用它,你会看到“它被保留作为以后使用(it is reserved for future use)”的错误提示。)如果你使用了成员函数指针,你最好使用typedef以防止混淆。
将函数指针指向型如float SomeClass::some_member_func(int, char *)的函数,你可以这样写:
my_memfunc_ptr = &SomeClass::some_member_func;
很多编译器(比如MSVC)会让你去掉“&”,而其他一些编译器(比如GNU G++)则需要添加“&”,所以在手写程序的时候我建议把它添上。若要调用成员函数指针,你需要先建立SomeClass的一个实例,并使用特殊操作符“->*”,这个操作符的优先级较低,你需要将其适当地放入圆括号内。
SomeClass *x = new SomeClass;
(x->*my_memfunc_ptr)(6, "Another Arbitrary Parameter");
//如果类在栈上,你也可以使用“.*”运算符。
SomeClass y;
(y.*my_memfunc_ptr)(15, "Different parameters this time");
不要怪我使用如此奇怪的语法——看起来C++的设计者对标点符号有着由衷的感情!C++相对于C增加了三种特殊运算符来支持成员指针。“::*”用于指针的声明,而“->*”和“.*”用来调用指针指向的函数。这样看起来对一个语言模糊而又很少使用的部分的过分关注是多余的。(你当然可以重载“->*”这些运算符,但这不是本文所要涉及的范围。)
一个成员函数指针可以被设置成0,并可以使用“==”和“!=”比较运算符,但只能限定在同一个类中的成员函数的指针之间进行这样的比较。任何成员函数指针都可以和0做比较以判断它是否为空。与函数指针不同,不等运算符(<, >, <=, >=)对成员函数指针是不可用的。
成员函数指针的怪异之处
成员函数指针有时表现得很奇怪。首先,你不可以用一个成员函数指针指向一个静态成员函数,你必须使用普通的函数指针才行(在这里“成员函数指针”会产生误解,它实际上应该是“非静态成员函数指针”才对)。其次,当使用类的继承时,会出现一些比较奇怪的情况。比如,下面的代码在MSVC下会编译成功(注意代码注释):
#include “stdio.h”
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 (*)
return 0;
}
奇怪的是,&DerivedClass::some_member_func是一个SomeClass类的成员函数指针,而不是DerivedClass类的成员函数指针!(一些编译器稍微有些不同:比如,对于Digital Mars C++,在上面的例子中,&DerivedClass::some_member_func会被认为没有定义。)但是,如果在DerivedClass类中重写(override)了some_member_func函数,代码就无法通过编译,因为现在的&DerivedClass::some_member_func已成为DerivedClass类中的成员函数指针!
成员函数指针之间的类型转换是一个讨论起来非常模糊的话题。在C++的标准化的过程中,在涉及继承的类的成员函数指针时,对于将成员函数指针转化为基类的成员函数指针还是转化为子类成员函数指针的问题和是否可以将一个类的成员函数指针转化为另一个不相关的类的成员函数指针的问题,人们曾有过很激烈的争论。然而不幸的是,在标准委员会做出决定之前,不同的编译器生产商已经根据自己对这些问题的不同的回答实现了自己的编译器。根据标准(第5.2.10/9节),你可以使用reinterpret_cast在一个成员函数指针中保存一个与本来的类不相关的类的成员函数。有关成员函数指针转换的问题的最终结果也没有确定下来。你现在所能做的还是像以前那样——将成员函数指针转化为本类的成员函数的指针。在文章的后面我会继续讨论这个问题,因为这正是各个编译器对这样一个标准没有达成共识的一个话题。
在一些编译器中,在基类和子类的成员函数指针之间的转换时常有怪事发生。当涉及到多重继承时,使用reinterpret_cast将子类转换成基类时,对某一特定编译器来说有可能通过编译,而也有可能通不过编译,这取决于在子类的基类列表中的基类的顺序!下面就是一个例子:
class Derived: public Base1, public Base2 // 情况 (a)
class Derived2: public Base2, public Base1 // 情况 (b)
typedef void (Derived::* Derived_mfp)();
typedef void (Derived2::* Derived2_mfp)();
typedef void (Base1::* Base1mfp) ();
typedef void (Base2::* Base2mfp) ();
Derived_mfp x;
对于情况(a),static_cast(x)是合法的,而static_cast(x)则是错误的。然而情况(b)却与之相反。你只可以安全地将子类的成员函数指针转化为第一个基类的成员函数指针!如果你要实验一下,MSVC会发出C4407号警告,而Digital Mars C++会出现编译错误。如果用reinterpret_cast代替static_cast,这两个编译器都会发生错误,但是两种编译器对此有着不同的原因。但是一些编译器对此细节置之不理,大家可要小心了!
标准C++中另一条有趣的规则是:你可以在类定义之前声明它的成员函数指针。这对一些编译器会有一些无法预料的副作用。我待会讨论这个问题,现在你只要知道要尽可能得避免这种情况就是了。
需要值得注意的是,就像成员函数指针,标准C++中同样提供了成员数据指针(member data pointer)。它们具有相同的操作符,而且有一些实现原则也是相同的。它们用在stl::stable_sort的一些实现方案中,而对此很多其他的应用我就不再提及了。
成员函数指针的使用
现在你可能会觉得成员函数指针是有些奇异。但它可以用来做什么呢?对此我在网上做了非常广泛的调查。最后我总结出使用成员函数指针的两点原因:
用来做例子给C++初学者看,帮助它们学习语法;或者
 
为了实现“委托(delegate)”!
 
成员函数指针在STL和Boost库的单行函数适配器(one-line function adaptor)中的使用是微不足道的,而且允许你将成员函数和标准算法混合使用。但是它们最重要的应用是在不同类型的应用程序框架中,比如它们形成了MFC消息系统的核心。
当你使用MFC的消息映射宏(比如ON_COMMAND)时,你会组装一个包含消息ID和成员函数指针(型如:CCmdTarget::*成员函数指针)的序列。这是MFC类必须继承CCmdTarget才可以处理消息的原因之一。但是,各种不同的消息处理函数具有不同的参数列表(比如OnDraw处理函数的第一个参数的类型为CDC *),所以序列中必须包含各种不同类型的成员函数指针。MFC是怎样做到这一点的呢?MFC利用了一个可怕的编译器漏洞(hack),它将所有可能出现的成员函数指针放到一个庞大的联合(union)中,从而避免了通常需要进行的C++类型匹配检查。(看一下afximpl.h和cmdtarg.cpp中名为MessageMapFunctions的union,你就会发现这一恐怖的事实。)因为MFC有如此重要的一部分代码,所以事实是,所有的编译器都为这个漏洞开了绿灯。(但是,在后面我们会看到,如果一些类用到了多重继承,这个漏洞在MSVC中就不会起作用,这正是在使用MFC时只能必须使用单一继承的原因。)
在boost::function中有类似的漏洞(但不是太严重)。看起来如果你想做任何有关成员函数指针的比较有趣的事,你就必须做好与这个语言的漏洞进行挑战的准备。要是你想否定C++的成员函数指针设计有缺陷的观点,看来是很难的。
在写这篇文章中,我有一点需要指明:“允许成员函数指针之间进行转换(cast),而不允许在转换完成后调用其中的函数”,把这个规则纳入C++的标准中是可笑的。首先,很多流行的编译器对这种转换不支持(所以,转换是标准要求的,但不是可移植的)。其次,所有的编译器,如果转换成功,调用转换后的成员函数指针时仍然可以实现你预期的功能:那编译器就没有所谓的“undefined behavior(未定义的行为)”这类错误出现的必要了(调用(Invocation)是可行的,但这不是标准!)。第三,允许转换而不允许调用是完全没有用处的,只有转换和调用都可行,才能方便而有效地实现委托,从而使这种语言受益。
为了让你确信这一具有争议的论断,考虑一下在一个文件中只有下面的一段代码,这段代码是合法的:
class SomeClass;
typedef void (SomeClass::* SomeClassFunction)(void);
void Invoke(SomeClass *pClass, SomeClassFunction funcptr) {
(pClass->*funcptr)(); };
注意到编译器必须生成汇编代码来调用成员函数指针,其实编译器对SomeClass类一无所知。显然,除非链接器进行了一些极端精细的优化措施,否则代码会忽视类的实际定义而能够正确地运行。而这造成的直接后果是,你可以“安全地”调用从完全不同的其他类中转换过来的成员函数指针。
为解释我的断言的另一半——转换并不能按照标准所说的方式进行,我需要在细节上讨论编译器是怎样实现成员函数指针的。我同时会解释为什么使用成员函数指针的规则具有如此严格的限制。获得详细论述成员函数指针的文档不是太容易,并且大家对错误的言论已经习以为常了,所以,我仔细检查了一系列编译器生成的汇编代码……
(待续)
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=33068
[收藏到我的网摘]   周翔发表于 2004年07月03日 14:49:00
特别推荐:
linux,C++人才哪去了.
趋势科技—网络安全软件及服务领域的全球领导者 趋势中国研发中心诚聘英才c
花旗软件招聘c++,java工程师
花旗软件目前主要为花旗集团亚太地区、中东及欧洲国家 提供软件开发和技术支持c
《C++编程艺术》在线阅读
本书揭示了C++程序员创建世界级软件的奥秘 CSDN读书频道独家奉献c
荣耀:C程序设计之四书五经
学习C语言的经典著作 CSDN读书频道独家奉献c
盛大网络诚聘技术人才!
诚聘软件精英,携手与您共创盛大新传奇c
# 一己薄剑 发表于2006-04-21 16:30:00  IP: 211.100.21.*
TrackBack来自《如何动态保存不同类的对象的成员函数的地址?--解决》:
即C 中委托的实现
# step_by_step 发表于2004-07-05 12:32:00  IP: 218.95.232.*
不错,详细
# freesmile 发表于2004-07-05 20:43:00  IP: 202.109.79.*
原文应该是
"For case (a), static_cast(x) will work, but static_cast(x) will fail."
# bullHEcow 发表于2004-07-05 16:43:00  IP: 222.95.25.*
“&DerivedClass::some_member_func是一个SomeClass类的成员函数指针”这个是指针,还是函数的地址
# bullHEcow 发表于2004-07-05 16:52:00  IP: 222.95.25.*
对于情况(a),static_cast(x)是合法的,而static_cast(x)则是错误的。
上面这两个static_cast(x) 有区别吗?
# bullHEcow 发表于2004-07-06 08:35:00  IP: 222.95.26.*
“&DerivedClass::some_member_func是一个SomeClass类的成员函数指针”这个是指针,还是函数的地址
# fellow 发表于2004-07-06 08:39:00  IP: 218.13.32.*
有点晕,扶扶我~~
# hifrog 发表于2004-07-06 08:48:00  IP: 61.162.67.*
应该是:
"对于情况(a),static_cast(x)是合法的,而static_cast(x)则是错误的。"——因为只能转化为子类的第一个父类。
排版时漏掉了,多谢兄弟提醒。
# abc 发表于2004-07-06 09:37:00  IP: 218.186.122.*
this is very very out dated.
1. pointer to class function will follow virtual keyword. ie, pointer to a virtual function in base class when called using a derived class, the function in drived class will be called.
2. member function pointer has different size as normal pointer. it contains infomation to deal with virtual functions and MI. it‘s senseless to to cast it to a different type or with a differnet(ie, no inheritance) class. the result IS UNDEFINED.
3. functor + arguent/caller bind will work the same way as closuer.
# aimar 发表于2004-07-06 10:36:00  IP: 61.145.247.*
谁说成员函数指针只是用来做教学的,在泛型编程中,函数指针和成员函数指针数组是经常用到的,另外,成员函数指针数组在多重继承的时候还有一个大陷阱,如果派生类定义了函数指针数组,而它的基类有虚函数,那么初始化的时候...
# 周星星 发表于2004-07-07 13:41:00  IP: 221.2.225.*
我不想给你泼冷水,但你这篇文章中全是似是而非的错误,我也懒得说,毕竟打字令人烦恼。
# aweay 发表于2004-07-08 09:57:00  IP: 218.21.42.*
这篇文章我也翻译过,原文本来就很麻烦,支持一下楼主
# my name  发表于2004-07-08 10:44:00  IP: 61.175.228.*
函数名本来就是指针,函数指针就是指向指针的指针,也就是双重指针,这句话“如果你的函数指针指向一个型如float some_func(int, char *)的函数,这样做就可以了:
my_func_ptr = some_func;
当你想调用它所指向的函数时,你可以这样写:
(*my_func_ptr)(7, "Arbitrary String");”
这样不是*my_func_ptr=some_func了吗?可是上面是
my_func_ptr=some_func呀?这样可以调用吗?
# yuk 发表于2004-07-09 20:20:00  IP: 61.129.192.*
见鬼去真他妈天书
# arrowwolfer 发表于2004-07-09 20:34:00  IP: 61.155.233.*
my_func_ptr = some_func;
当你想调用它所指向的函数时,你可以这样写:
(*my_func_ptr)(7, "Arbitrary String");”
这样不是*my_func_ptr=some_func了吗?可是上面是
my_func_ptr=some_func呀?这样可以调用吗?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
本来
(*my_func_ptr)(7, "Arbitrary String");
也可以写成这样:
my_func_ptr(7, "Arbitrary String");
# bullHEcow 发表于2004-07-09 13:26:00  IP: 222.95.24.*
to:楼上
“函数名本来就是指针”我觉的这句话不对
# stevenxu 发表于2004-07-09 14:51:00  IP: 219.82.170.*
这篇文章好像是个平时主要用Java或者别的什么语言的人写的。
# ChenA 发表于2004-07-09 17:23:00  IP: 220.248.6.*
我不想给你泼冷水,但你这篇文章中全是似是而非的错误,我也懒得说,毕竟打字令人烦恼。
那你就什么也别说,最烦你这种人。
# donkey0811 发表于2004-07-11 11:12:00  IP: 219.145.162.*
真的是很难懂诶
# studyself 发表于2004-07-13 13:44:00  IP: 202.114.3.*
好贴!
# tiger 发表于2004-07-13 16:55:00  IP: 221.232.181.*
说的对
# 111 发表于2004-07-13 11:09:00  IP: 221.6.17.*
不要用什么面向对象的东西来吓唬人,C++ 是面向效率的,C++中的函数指针要比代理之类的东西灵活得多,也高效的多。
# hifrog 发表于2004-07-13 21:08:00  IP: 61.162.66.*
因为复习考研,好久没上网了,今天到自己的Blog来看到有很多兄弟来给我加油,我很是感激。我想在这里简要说一说作者的观点:
1.成员函数指针本身是个好东西。问题出在成员函数所在类的继承属性上,如果这个类是多重继承类或虚拟继承类,在使用成员函数指针时会出现问题。而C++的标准也没有对这个问题进行明确的回答。
2.委托是一个更好的东西。委托的效率在“实质上”比成员函数指针高的多,但在“实际上”委托的被“一些设计者”变成了高层次的、庞大的、缓慢的东西。
如果各位朋友真的要读这篇文章,我想最好能把这篇文章从头到尾读完。
# jeff 发表于2004-07-14 16:34:00  IP: 218.19.114.*
其实用模板就可以实现“委托”。这是我在写事件处理器时用的一个模板类
template
class CEventHandler
{
public:
typedef CEventHandler EVENTHANDLER;
typedef void (HandlerClass::*LPHANDLEFUNC)(const CEventItem& refEvt);
DWORD m_dwEvtID;
LPHANDLEFUNC m_lpHandleFunction;
CEventHandler(const EVENTHANDLER& refEvtHandler)
{
m_dwEvtID=refEvtHandler.dwEvtID;
m_lpHandleFunction=refEvtHandler.lpHandleFunc;
};
const EVENTHANDLER& operator = (const EVENTHANDLER& refEvtHandler)
{
if(&refEvtHandler!=this)
{
m_dwEvtID=refEvtHandler.dwEvtID;
m_lpHandleFunction=refEvtHandler.lpHandleFunc;
}
return *this;
};
CEventHandler(DWORD dwEvtID,LPHANDLEFUNC lpHandleFunc)
{
m_dwEvtID=dwEvtID;
m_lpHandleFunction=lpHandleFunc;
};
CEventHandler()
{
m_dwEvtID=-1;
m_lpHandleFunction=NULL;
};
virtual ~CEventHandler()
{
m_dwEvtID=-1;
m_lpHandleFunction=NULL;
};
};
# jeff 发表于2004-07-14 16:41:00  IP: 218.19.114.*
贴错代码了。下面这个才对^_^
template
class CPacketHandlerT : public CPacketHandler
{
typedef void (HandlerClass::*LPHANDLEFUNC)(const CCMPPPacket* lpPacket);
private:
HandlerClass& m_refHandlerClassInstance;
LPHANDLEFUNC m_lpHandleFunction;
public:
virtual void Handle(const CCMPPPacket* lpPacket)
{
ASSERT(m_lpHandleFunction);
if(m_lpHandleFunction)
(m_refHandlerClassInstance.*m_lpHandleFunction)(lpPacket);
}
CPacketHandlerT(HandlerClass& refClassInstance,LPHANDLEFUNC lpHandleFunc)
:m_refHandlerClassInstance(refClassInstance),m_lpHandleFunction(lpHandleFunc)
{
ASSERT(lpHandleFunc);
};
CPacketHandlerT(HandlerClass* pClassInstance,LPHANDLEFUNC lpHandleFunc)
:m_refHandlerClassInstance(*pClassInstance),m_lpHandleFunction(lpHandleFunc)
{
ASSERT(lpHandleFunc);
};
virtual ~CPacketHandlerT()
{
m_lpHandleFunction=NULL;
};
};
# zihan 发表于2004-07-16 22:00:00  IP: 61.235.65.*
楼上贴的代码其实和楼主的差不多,只是通过摸板更加通用,当然比楼主要好一点。
这是一篇不错的文章,可是我见过很多人在csdn上面发贴,都受到一些人的乱骂,不知道他们是真的很厉害,还是要显示自己水平很高,也许对你来说很简单,甚至是错误的,可是毕竟对作者来说是一片好意,我觉得我们在提意见的时候应该注意方式。
# studyself 发表于2004-07-17 14:16:00  IP: 202.114.30.*
好贴!
# a b c 发表于2004-07-19 19:33:00  IP: 61.149.68.*
虽然我还看不大懂楼主的文章,但是,我还是受益非浅。
# ANGUS 发表于2004-07-18 15:53:00  IP: 202.213.113.*
(*my_func_ptr)(7, "Arbitrary String");
也可以写成这样:
my_func_ptr(7, "Arbitrary String");
两种方法都可以。
# guolb 发表于2004-07-20 13:49:00  IP: 61.51.131.*
函数名就是函数的起始地址,
函数指针也是函数的起始地址,并不是指向指针的指针。
数组变量和数组指针应该和这个相似的把。
# 星哥 发表于2004-09-27 17:53:00  IP: 165.170.128.*
我现在看到那个叫周星星的人留言我就觉得有点悲哀,他自己也不嫌寒碜,要不然你就匿名,要不然就说出点道理,不要看谁都不顺眼,我敢打赌让那个星星牛人他翻不出楼主这么好的文章,做人要厚道,不要随便给比人泼冷水。
# coordinate 发表于2005-06-16 14:28:00  IP: 61.186.252.*
"对于情况(a),static_cast(x) ... MSVC会发出C4407号警告,而Digital Mars C++会出现编译错误。"
我用VC6做了如下试验,为什么没有任何的警告和错误?
#include "stdafx.h"
#include
class Base1
{
public:
void b1_test(void);
};
void Base1::b1_test()
{
cout<<"Base1"<}
class Base2
{
public:
void b2_test(void);
};
void Base2::b2_test()
{
cout<<"Base2"<}
class Derived1:public Base1,public Base2
{
public:
void b1_test(void);
void d1_test(void);
};
void Derived1::b1_test()
{
cout<<"Derived test"<}
void Derived1::d1_test()
{
cout<<"d1_test"<}
class Derived2:public Base2,public Base1
{
public:
void b2_test(void);
};
void Derived2::b2_test()
{
cout<<"Derived2 test2"<}
typedef void (Base1::*base1_func)();
typedef void (Base2::*base2_func)();
typedef void (Derived1::*derived1_func)();
typedef void (Derived2::*derived2_func)();
void fun(Base1 *b)
{
b->b1_test();
}
int main(int argc, char* argv[])
{
derived1_func x;
static_cast(x);
static_cast(x);
return 0;
}
# newhappy 发表于2005-11-23 10:18:00  IP: 218.80.155.*
Let‘s exchange exp.
http://spaces.msn.com/members/newhappy/Blog/cns!1p2t2ZDZbz-qCPmiguyJiWgg!207.entry
# 千里马肝 发表于2005-11-24 10:10:00  IP: 218.80.200.*
why not use boost::function?
# simple 发表于2005-11-25 11:39:00  IP: 202.204.161.*
是不是因为我在某个地方的留言提到这个翻译把这篇好久以前的文章给折腾到csdn的首页了?
嘿嘿
又有多少人愿意去考虑 原作者提出的多次委托的进一步设计?
以及事件返回值被多个委托引用的情形?
# 网中鱼 发表于2005-11-25 12:35:00  IP: 218.9.217.*
支持楼主,不错的文章,总有人对别人的奉献精神说三道四,如果真觉得自己高,拿出点东西来让大家看看。到国外技术论坛看看,人家都在正常讨论问题,哪有象周星星这种人自己不做出贡献还在贬低别人的。强烈BS!
# Town 发表于2005-12-01 15:07:00  IP: 218.107.210.*
我认为,
书中所说的指针,准确地理解应该是指针变量才对,
该指针变量的值存储了相应变量的地址(或编译器通过该值可以取得相应变量的地址)。
所以,函数指针不等于函数地址(函数的首地址)。