用这个做关键词在google上搜索会得到一大堆文章,那些用c++实现的sig/slot库基本上都是以这个为基础,花了几个小时来检索,最后还是没有 找到合适的代码,我向来厌恶通用的完整的框架,觉得太复杂了,很难弄明白 例如ACE之类,网络程序框架从来都是自己手工写,代码里面充斥CSimpleSocket,CSimpleUDP,CSimpleSQL这样的名词,除了STL之外,几乎从不用模板,看着那些大而全的实现就发怵,不 得不自己从头造车轮 事情的起因是这样,我在程序里面需要实现一个函数链,研究了一下,有多种实现方法。

  • c的方案 定义一个函数指针,然后在声明一个指针数组,剩下的就是逻辑的实现了

  • c++的方案 当然也可以延续c的做法,不过OOP的基本代码单元是类,要定义函数指针,带this指针的类成员函数是不行的了,哪只能定义一些静态成员函数,但这样的实现限制太多,何必披上c++的马甲呢。习惯的做法是声明一个接口,比如下面这个 struct IFoorbar { virtual void run()=0; };然后每个需要加入到函数链的宿主类都实现这个接口,cpp支持多重继承的好处就显现在这里.但是这样的做法也有弊端,就是每个类只能提供一个函数,如果想要在一个类里面提供多个函数,就得重新定义接口了,要加上一个功能标签。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
struct IFoorbar
{
	virtual void run(int func_tag)=0;
};
class Some:public IFoorbar
{
public:
	virtual void run(int func_tag)
	{
		switch(func_tag):
		{
			case 1:
			//do something 1
			break;
			case 2:
			//do something 2
			break;
		}
	}
};

这样做固然可行,但是太繁琐,而且在具体使用中,还有些小的缺陷,这就引出了委托。所谓委托,简而言之,就一个类成员变量的通用指 针的类型。在c#里,这个是自带的功能,Delphi也能够轻易的实现,因为它支持定义类成员函数的类型,这个也是Delphi仅有的几处在语法层面优于 c++的地方 type TCallBack = procedure of object;

委托的实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
struct ICallBack
{
	virtual void operator()()const =0;
	virtual ~ICallBack(){};
};
template
class Delegate:public ICallBack
{
	typedef void (ObjectType::*FunctionPtr)(void);
	public:
		Delegate(ObjectType * obj,FunctionPtr func):m_obj(obj),
		m_func(func)
		{
		}
		virtual void operator()() const
		{
			(m_obj->*m_func)();
		}
		operator=(const Delegate & other)
		{
			m_func = other.m_func;
			m_obj = other.m_obj;
		}
	private:
		FunctionPtr m_func;
		ObjectType * m_obj;
};
//-----------------------for test----------------------------
class A
{
public:
	void print()
	{
		printf("call me a\n");
	}
	};
	void run(const ICallBack *c)
	{
		if(c)(*c)();
	}
}
int _tmain(int argc, _TCHAR* argv[])
{
	A a;
	Delegate dg(&a,&A::print);
	run(&dg);
	return 0;
}

简单的说明

这个委托类是对应的是一个无参数和无返回值的类成员函数,可以根据实际情况做扩展 定义接口ICallBack的目的就是为了能够横向的切分代码,能够很简单的定义一个类型指针,而不需要加上template list,如果没有定义这个接口,那么上面的run函数就得定义如下 void run(const Delegate < A > &d) { …… }