C++指针探讨(三)成员函数指针
副标题#e#
C语言的指针相当的机动利便,但也相当容易堕落。很多C语言初学者,甚至C语言老鸟都很容易栽倒在C语言的指针下。但不行否定的是,指针在C语言中的位置极其重要,也许可以过火一点的来说:没有指针的C措施不是真正的C措施。
然而C++的指针却经常给我一种束手束脚的感受。C++比C语言有更严格的静态范例,越发强调范例安详,强调编译时查抄。因此,对付C语言中最容易错用的指针,更是不能放过:C++的指针被分成数据指针,数据成员指针,函数指针,成员函数指针,并且不能随便彼此转换。并且这些指针的声明名目都纷歧样:
尚有一个更重要的区别是,指针所占的空间也纷歧样了。纵然在32位系统中,所占的空间也有大概是4字节、8字节、12字节甚至16字节,这个依据平台及编译器,有很大的变革。
尽量C++中仍然有万能指针void*,但它却属于被批斗的工具,并且再也不能“万能”了。它不能转换成成员指针。
这样一来,C++的指针就变得很难过:我们需要一种指针可以或许指向同一范例的数据,不管这个数据是普通数据,照旧成员数据;我们更需要一种指针可以或许指向同一范例的函数,不管这个函数是静态函数,照旧成员函数。可是没有,至少以后刻的C++尺度中,还没有看到。
自从有了类,我们开始凭据 数据+操纵 的方法来组织数据布局;自从有了模板,我们又开始把 数据 和 算法 疏散,以便重用,实在够折腾人的。但不管怎么折腾,此刻大大都函数都不再只身,都嫁给了类,进了围城。但是我们仍然需要可以或许自由挪用这些成员函数。
思量一下windows下的按时挪用。SetTimer函数的原型是这样的:
UINT_PTR SetTimer(
HWND hWnd,
UINT_PTR nIDEvent,
UINT uElapse,
TIMERPROC lpTimerFunc
);
个中,参数就不表明白,这个函数预计大大都windows开拓人员都知道。lpTimerFunc是个会被按时挪用的函数指针。如果我们不通过WM_TIMER动静来触发按时器,而是通过lpTimerFunc来按时事情,那么我们就只能利用普通函数或静态函数,而无论如何都不能利用成员函数,哪怕通过静态函数转调也不可。
再思量一下线程的建设:
uintptr_t _beginthread(
void( *start_address )( void * ),
unsigned stack_size,
void *arglist
);
start_address仍然只支持普通函数。不外这回好了,它答允回调函数一个void*参数,它将会arglist作为参数来挪用start_address.于是,智慧的C++措施员,就操作arglist通报this指针,从而操作静态函数乐成的挪用到了成员函数了:
class mythread
{
public:
static void doit(void* pThis)
{
((mythread*)pThis)->doit();
}
void doit(...){}
};
main()
{
...
mythread* pmt = new mythread;
_beginthread(&mythread::doit, 0, (void*)pmt);
...
}
#p#副标题#e#
可是显然,C++措施员必定不会因此而满意。这里头有很多被C++批驳的不安宁因素。它利用了C++中被认为不安详的范例转换,不安详的void*指针,等等等等。但这是系统为C语言留下的挪用接口,这也就认了。那么如果,我们就在C++措施中如何来挪用成员函数指针呢?如下例,我们规划对vector中的所有类挪用其指定的成员函数:
#include <vector>
#include <algorithm>
#include <functional>
#include <iostream>
using namespace std;
class A
{
int value;
public:
A(int v){value = v;}
void doit(){ cout << value << endl;};
static void call_doit(A& rThis)
{
rThis.doit();
}
};
int main()
{
vector<A> va;
va.push_back(A(1));
va.push_back(A(2));
va.push_back(A(3));
va.push_back(A(4));
//要领1:
//for_each(va.begin(), va.end(), &A::doit); //error
//要领2:
for_each(va.begin(), va.end(), &A::call_doit);
//要领3:
for_each(va.begin(), va.end(), mem_fun_ref<void, A>(&A::doit));
system("Pause");
return 0;
}
要领1,编译不能通过。for_each只答允具有一个参数的函数指针或函数工具,哪怕A::doit默认有一个this指针参数也不可。不是for_each没思量到这一点,而是基础做不到!
要领2,显然是受到了beginthread的开导,利用一个静态函数来转挪用,哈哈乐成了。可是不爽!这不是C++.
要领3,呼,好不容易啊,终于用mem_fun_ref包装乐成了成员函数指针。
#p#分页标题#e#
好像要领3不错,又是范例安详的,又可以通用——慢着,首先,它很丑,哪有挪用普通C函数指针那么大度啊(见要领2),用了一大串包装,又是尖括号又是圆括号,还少不了&号!其次,它只能包装不高出一个参数的函数!尽量它在for_each中够用了,可是你要是想用在高出一个参数的场所,那只有一句话:不行能的任务。
是的,在尺度C++中,这是不行能的任务。但工作并不老是灰心的,至少有很多第三方库提供了逾越mem_fun的包装。如boost::function等等。可是它也有限制:它所支持的参数仍然是有限的,只有十多个,尽量够你用的了;同样,它也是丑恶的,永远不要想它可以或许简朴的用&来搞定。
也许,以失去瑰丽的价钱,来调换质量上的担保,这也是C++对付函数指针的一种无奈吧……
等候C++0x版本。它通过可变模板参数,可以或许让mem_fun的参数到达无限个……
————————
BTW: C++Builder扩展了一个要害字 closure ,答允成员函数指针如同普通函数指针一样利用。也许C++0x能思量一下……