Wednesday, December 20, 2006

c++的delegate

最近在尝试自己实现一个OpenGL的GUI,进展缓慢,不过倒是老能学到新的东西。昨天在想如何处理消息的时候遇到点问题。本来我是想让代码风格类似java swing,这样就需要定义一些Listener监听器,比如MouseListener和KeyListener。这些Listener中分别定义响应消息的函数接口。实现这些接口的类继承这些Listener。同时在每一个组件Component里都有一个Listener的数组,通过向这个组件添加Listener,就可以定义这个组件的行为了。
简单的说就是这样:
class EventListener{}

class MouseListener: public EventListener
{//一些接口}

class Component
{
std::vector mouseListenerList;
addMouseListener(MouseListener* listener)
{
mouseListenerList.push_back(listener);
};
}

比如现在有个按钮button控件,还有个dialog窗口容器,响应事件就可以这样写:
class Button:public Component
{}

class Dialog:public Component, public MouseListener
{
Button button;
Dialog()
{
button.addMouseListener(this);
};
//实现的MouseListener中定义的一个接口
void onMouseMove(MouseEvent &e)
{};
}

为什么在每个Component中都要定义一个Listener的数组呢?允许一个Component有多个Listener来处理事件有什么意义呢?原因是这样做可以把一个控件公共的事件响应和私有的事件响应分开处理。比如说,每一个按钮都有一个公共的行为:鼠标按下的时候按钮变亮了,不管这个按钮功能是什么,只要按下就变亮,这是公共的事件响应。同时每个按钮又有不同的功能,有的负责程序的退出,有的负责窗口打开,这些都是私有的事件响应。所以多个Listener可以把这两种响应分别来处理。这样使用的人不用重复实现按钮变亮的公共操作,更不用关心它是怎么实现的。
比如说这样:
class Button:public Component,public MouseListener
{
Button()
{
//每一个按钮都能在按下的时候变亮
addListener(this);
}
void onMousePressed(MouseEvent &e)
{
//继承MouseListener接口,实现按钮变亮
};
}

class Dialog:public Component,public MouseListener
{
Button button;
Dialog()
{
//响应窗口关闭
button.addListener(this);
}
//实现MouseListener,负责窗口关闭
void onMousePressed(MouseEvent &e)
{
//关闭窗口
}
}

但是这样写也有问题,如果一个Dialog中包含有个多个控件,而这些控件都要响应一个事件,这样就比较麻烦。比如:
class Dialog:public Component,public MouseListener
{
Button button1;
Button button2;
Dialog()
{
button1.addListener(this);
button1.addListener(this);
}
void onMouseClicked(MouseEvent &e)
{
//所有按钮都要靠这一个函数来响应
};
}

这里面两个按钮都通过一个函数来响应。编程的时候要在这个函数中写代码判断这个事件到底是谁发出的,然后相应地处理。在运行的时候也要不断判断,损失了一些性能。在java中允许这样来添加Listener:
jButton1.addMouseListener(new java.awt.event.MouseAdapter(){
public void mouseClicked(java.awt.event.MouseEvent evt){
onMouseClicked(evt);}});

如果能这样写,就很方便了,可以轻易地把响应不同按钮的函数给区分开。可惜c++里不行。我又想到.net:要是能直接添加函数指针到数组中岂不是更简单。c++里倒是有函数指针,但是面向对象的程序中,大部分函数是类中的成员函数,而成员函数的指针必须和类挂钩,类A的成员函数指针和类B的成员函数指针不能相互传递,即便这两个成员函数的返回值和参数都是一样的。而且即使两个类之间是相互继承的关系,这种成员函数指针也是不能相互拷贝的。比如这样就是不行的:
class A
{
void (A::*function)();
}

class B
{
B()
{
//不行
function=&B::hello;
};
void hello(){};
}

这就费事了,因为我需要Component类中能有一个存放函数指针的数组,这些函数指针对应着处理事件的函数,同时我希望这个函数指针数组可以保存来自不同类的成员函数指针。
幸亏我在codeproject找到了这篇牛文:http://www.codeproject.com/cpp/FastDelegate.asp
这篇文章的作者实现了在c++下的delegate,原理我还没完全明白,不过这东西用起来超爽。
它定义了从FastDelegate0到FastDelegate8的几个类,可以和不同类中的成员函数绑定,其中最后面的数字表明所绑定函数的参数个数。
比如:
class A
{
void helloA(int a){};
}

class B
{
void helloB(int b){};
}

A a;
B b;
FastDelegate function[10];
function[0].bind(&a,&A::helloA);
function[1].bind(&b,&B::helloB);

No comments: