Event support is already one of the subsequent language syntaxes such as Delphi, Java, and C #, but it is not shown in C ++. Different compilers use their own methods to support events. For example, Borland C ++ Builder provides event support through the extended syntax to implement VCL; the MFC event is a table-driven static event generated by the wizard during design, and does not support running. Both implementations can only work in specific scenarios and are difficult to transplant. Therefore, we need an event mechanism completely based on the C ++ syntax. We cannot use the compiler's extension keywords for the C ++ syntax. In addition, this mechanism should be dynamic, that is, you can change the event attribute at runtime. This attribute usually refers to the address of the method or function associated with the event processing process, events with dynamic personality attributes are more flexible and easy to use. To achieve this goal, you can refer to Delphi, Java, C #, including bcb vcl, And the implementation or representation mechanism of events in these languages (or development tools, you can also refer to the connection point technology of COM. It is easy to find that they have one thing in common: all objects are sourced from a public base class, especially BCB. if the object is not inherited from TObject, the event cannot be used. You can also find some interesting things by referring to the implementation of the COM connection point, especially the method of calling the connection point event. At the same time, the method of creating an event table using MFC is also worth learning. With the public base class, the derived object can be mapped up, so that the event presentation can be uniformly defined. There is another important significance in using the public base class: when we get the method address of an object in C ++, the actually obtained object method is offset in the virtual table, the offsets of methods derived from objects of the same base class are different, that is, their virtual tables have a uniform layout, while the methods derived from objects of different base classes may be the same. Before proceeding to the next discussion, let's take a look at how to call object methods through pointers in C ++. In C ++, you can obtain the method address of an object in the following form:
& Object: MethodThe obtained address is actually the 16-bit offset of the method in the virtual table of the object. Therefore, you need an instance pointer of the object to call the method correctly. You can use the following statement to call the method in the instance:
(Object instance pointer-> * method address) (parameter)Generally, we need to map the method address to a common representation:
Static_cast <Common Method Representation> (& object: method)For example, for the Methods A: func1, B: func2 of two classes A and B, when they are derived from the same base class, they have different offsets in the virtual table, one may be 1, and the other may be 5, but they will never be the same. Assume that the obtained address is addr1 and addr2. Then there will be: addr1 = static_cast <performance definition> (& A: func1); addr2 = static_cast <performance definition> (& B: func2); anytime, if two methods are called in the following form, the method is correct: (Object x pointer-> * addr1 )(); // This will call the method at offset 1 (Object y pointer-> * addr2) (); // This will call the method at offset 5, but when they are derived from different base classes, the static_cast ing in the unified form cannot be performed, and the method may have the same offset. In this way, both calls attempt the same method, which leads to an error. The common base class can be used to associate the event handling method attributes with any defined methods, so that the event attributes can be modified at runtime to implement the dynamic event mechanism. Of course, when setting the Event Correlation Processing Method-usually called the event handle (handler)-, we also need to save an instance pointer of an object with this method. In this way, when an event occurs, we can call the correct method on the correct object instance to complete the correct event processing. The following is the implementation code based on the above method, using the naming conventions of bcb and the doxgen annotation style:/*** base class */class tobject {}; /*** event parameter class */template <class T> class teventarg: Public tobject {T value; public: teventarg (): Value () {} teventarg (t x) {value = x;} teventarg (const teventarg & ref) {value = ref;} teventarg <t> & operator = (t x) {value = x; return * This ;} teventarg <t> & operator = (teventarg <t> X) {value = x; return * This;} operator T () {r Eturn value ;};/*** event class */template <class T> class tevent: Public tobject {public: /*** event handle type definition */typedef void (tobject: * teventhandler) (teventarg <t> []); Private: /*** the array of handles for storing Event Processing Methods * generally, you only need to save an event processing method. * This is used to demonstrate how an event inspires multiple processing handles, array is used to * save all handles */vector <pair <tobject *, teventhandler> _ handlerarray; public: /*** clear handle of all event handling methods */void clear () {_ handlerarray. clear ();}/*** remove event handling method handle */void R Emove (pair <tobject *, teventhandler> handler) {// generally, there are only one or more events, so for the sake of simplicity and purpose, the for loop for (vector <pair <tobject *, teventhandler> >:: iterator it = _ handlerarray. begin (); it! = _ Handlerarray. end (); It ++) {If (* it ). first = handler. first & * (int *). second = * (int *) handler. second) {It = _ handlerarray. erase (IT) ;}}/ *** handle assignment operation to clear existing handles, replace * @ see add */tevent <t> & operator = (pair <tobject *, teventhandler> handler) {clear (); _ handlerarray. push_back (handler); return * This;}/*** add event handling method handle. In actual situations, it is easier than operator + = */void add (tobject * object, teventhandler Handler) {_ handlerarray. push_back (make_pair <tobject *, teventhandler> (object, Handler ));} /*** add event handling method handle */tevent <t> & operator + = (pair <tobject *, teventhandler> handler) {_ handlerarray. push_back (handler); return * This;}/*** Delete handle */tevent <t> & operator + = (pair <tobject *, teventhandler> handler) {remove (handler); return * This;}/*** trigger event */void fire (teventarg <t> ARGs []) {/*** cyclically fires all events */For (vector <pair <tobject *, teventhandler> >:: iterator it = _ handlerarray. Begin (); it! = _ Handlerarray. End (); It ++) {If (* It). First! = NULL & (* It). Second! = NULL) {// here is how to call the event handle to generate the event to stimulate the // For the sake of indication, write three steps tobject * object = (* it ). first; teventhandler handler = (* it ). second; (Object-> * Handler) (ARGs); // (* it ). first-> * (* it ). second) (ARGs); // you can take a short step} // If} // For} // method fire }; /*** event excitation source */template <class T> class tmyeventfirer: Public tobject {public: // event tevent <t> myevent; /// method void fireevent (teventarg <t> ARGs []) {myevent. fi Re (ARGs) ;}};/*** one of the objects for event processing */Class A: Public tobject {public: A () {} public: /*** Event Processing Method */void func (teventarg <int> ARGs []) {cout <"A: FUNC:" <(INT) ARGs [0] <Endl ;};/*** the second object for event processing */Class B: Public tobject {public: B () {} public: /*** Event Processing Method */void func (teventarg <int> ARGs []) {cout <"B: FUNC:" <(INT) ARGs [0] <Endl ;}; int _ tmain (INT argc, _ tchar * argv []) {tmyeventfirer <int> firer; // event source A; // event processing object B; // event processing object // Add the event processing firer of object. myevent + = make_pair <tobject *, tevent <int>: teventhandler> (& A, static_cast <tevent <int>: teventhandler> (& A: func )); // Add the event processing firer of object B. myevent + = make_pair <tobject *, tevent <int >:: teventhandler> (& B, static_cast <tevent <int >:teventhandler> (& B: func )); // re-add event processing for object a // because there is no discriminative uniqueness, event processing for object A will be triggered for firer. myevent. add (& A, static_cast <tevent <int>: teventhandl ER> (& A: func); // prepare the parameter teventarg <int> ARGs [1]; // initialize ARGs [0] = 999; // trigger event firer. fireevent (ARGs); Return 0;} this rough example should be sufficient to demonstrate how to implement the dynamic event mechanism. Complete the rest on your own. Free please visit: playful network http://www.qpix.cn