易于使用的事件






4.37/5 (19投票s)
2005年10月30日
2分钟阅读

51144

409
本文描述了一个用标准 C++ 编写的易于使用的事件类。
引言
本文描述了一个用标准 C++ 编写的易于使用的事件类。它非常简单,易于理解和使用。你几乎没有犯错的机会。参数数量没有限制。你可以连接到成员函数、函数对象或全局函数。我已经修改了它,使其能够使用 GCC 编译。演示文件夹中包含一个 Dev-C++ 项目文件。
一个非常简单的例子
// example1 #include <iostream> #include <string> #include "Event.hpp" using namespace std; class Edit { string s_; public: // define event type 'TextChanged' EVENT_TYPE(TextChanged, (string s), (s)); TextChanged changed; void set(string s) { s_ = s; // fire 'changed' event when the content of Edit is altered changed.fire(s_); } }; class Dialog { Edit edit_; public: void OnEditChanged(string s) { cout << "text in edit is : " << s << endl; } Dialog() { //edit_.changed.addListener(this, OnEditChanged); // vc7.1 edit_.changed.addListener(this, &Dialog::OnEditChanged); } void input(string s) { edit_.set(s); } }; void example1() { Dialog dlg; dlg.input("some text"); dlg.input("another text"); }
如上例所示,它非常简单。你需要做的就是定义一个事件,然后调用它的 addListener
方法将其与目标对象连接起来。fire
方法用于触发一个事件。
唯一令人困惑的是
EVENT_TYPE(TextChanged, (string s), (s));
这代表什么意思?
实际上很简单,宏展开后的结果是一个名为 TextChanged
的类,例如
class TextChanged { public: void fire(string s); };
(string s)
和 (s)
的括号不应省略。(string s)
称为参数列表,它指定了 fire()
方法接受的参数的类型和名称。(s
) 称为参数列表,它指定如何调用 fire()
方法,例如 fire(s)
。
连接管理
EVENT_TYPE(SomeEvent, (int x, int y), (x, y)); SomeEvent event1; // function object class Foo { public: void operator()(int a, int b) { cout << "function object: " << a << '+' << b << '=' << a + b << endl; } }; // ordinary function void bar(int a, int b) { cout << "function: " << a << '+' << b << '=' << a + b << endl; } void example2() { Foo foo1; // connect to a function object SomeEvent::Connection c1(event1.addListener(foo1)); SomeEvent::Connection c2; // connect to an ordinary function c2 = event1.addListener(bar); //SomeEvent::Connection c3(c1); // error: copy construction is not permitted //c1 = c2; // error: assignment is not permitted event1.fire(1, 2); { Foo foo2; SomeEvent::Connection c4(event1.addListener(foo2)); // create an anonymous connection event1.addListener(bar); event1.fire(3, 4); // c4 break up automaticly } c1.disconnect(); event1.fire(5, 6); } // connection's lifetime longer than event, no problem! void example3() { SomeEvent::Connection c; { SomeEvent event2; c = event2.addListener(bar); event2.fire(9, 9); } // you can invoke c.disconnect() explicitly or // rely on c's destructor to do it }
addListener
在事件和目标对象之间建立连接,将双方链接起来。
如果你将 addListener
的返回值分配给一个连接对象,当该连接对象的生命周期结束时,事件和目标对象之间的连接将中断,因此双方之间不再有关系。
在连接对象上调用 disconnect
方法具有相同的效果。如果你没有将 addListener
的返回值分配给某个连接对象,则在事件对象生命周期结束之前,事件和目标对象之间存在一个匿名连接,该连接将中断。
事件和目标对象之间的连接不会自动中断。如果连接没有中断,而目标对象不再存在,则触发事件会导致问题,因此请务必小心。
为了确保目标对象生命周期结束后,目标对象和事件之间没有连接,你可以选择在目标对象死亡之前调用连接对象的 disconnect
方法,或者将连接作为目标对象的一部分。在大多数情况下,后者更可取。
历史
- 错误修复 - 2005-11-07。