Many-to-many
The next question is, can we perform multiple operations after clicking the reload button? That is, how many-to-many relationship is implemented between the signal and the slot?
In fact, we only need to use a common linked list to easily implement this function. For example, the following implementation:
class MultiAction : public AbstractAction // ...an action that is composed of zero or more other actions; // executing it is really executing each of the sub-actions{public: // ... virtual void execute();private: std::vector<AbstractAction*> actionList_; // ...or any reasonable collection machinery};void MultiAction::execute(){ // call execute() on each action in actionList_ std::for_each( actionList_.begin(), actionList_.end(), boost::bind(&AbstractAction::execute, _1) );}
This is one of the implementations. Don't think this kind of implementation doesn't seem to be quite good. In fact, we find this is a very simple method. At the same time, don't worry about the namespaces std: and boost: In our code. You can use another class to emphasize that this is only a possible implementation. Now, one of our actions can be connected to multiple buttons. Of course, it can also be used by other actions. Now we have a many-to-many mechanism. Replace AbstractAction * With boost: shared_ptr <AbstractAction> to resolve the attribution problem of AbstractAction and maintain the original many-to-many relationship.
There will be many classes!
If you use the above mechanism in the actual project, many will find that we must define a class for each action, which will inevitably lead to class explosion. So far, all the implementations we mentioned above have this problem. However, we will discuss this issue later. Don't worry about it now!
Special! Special!
When we start to work, we can reuse the button class by assigning each Button to different actions. This is actually a special feature. However, our problem is that the specialization of action is placed in a fixed class level, where these Button classes are located. This means that our actions are difficult to be reused on a larger scale, because each action is actually bound to the Button class. So, in another way, can we put this special feature into the connection between the signal and the slot? In this way, neither action nor button needs to be made special.
Function object
It is quite useful to encapsulate functions of a class. In fact, the existence of our Action class is to encapsulate the execute () function. Other functions are useless. This is still common in C ++. In many cases, we use the features of ++ to re-encapsulate functions to make the class behavior look like a function. For example, we can overload the operator () operator to make the class look like a function:
class AbstractAction{public: virtual void operator()() = 0;};// using an action (given AbstractAction& action)action();
In this way, our class looks like a function. In the previous Code, for_each must also be changed accordingly:
// previouslystd::for_each( actionList_.begin(), actionList_.end(), boost::bind(&AbstractAction::execute, _1) );// nowstd::for_each( actionList_.begin(), actionList_.end(), boost::bind(&AbstractAction::operator(), _1) );
Now, we have more options for implementing the Button: clicked () function:
// previouslyaction_->execute();// option 1: use the dereferenced pointer like a function(*action_)();// option 2: call the function by its new nameaction_->operator()();
It looks troublesome. Is it worth doing this?
Next we will try to explain the purpose of the signal and slot. It seems that rewriting the operator () operator is too much, and it is not worth doing so. However, you must know that the more available solutions you provide on some issues, the more convenient we can write more concise code. By standardizing some classes, just as we want to make function objects look more like functions, we can make them more suitable for reuse in some environments. This is especially important when you use template programming, Boost. Function, bind, or template metaprogramming.
This is a part of the answer to the importance of the connection without requiring more special features. The template provides such a mechanism that makes code that adds special parameters not so difficult to be made special, just like our function objects. The template features are transparent to users.
Loose coupling
Now, let's review our previous practices.
We persistently seek a method that can call different functions in the same place. This is actually one of the built-in functions of C ++. Through the virtual keyword, of course, we can also use function pointers. When the function to be called does not have a proper signature, we wrap it into a class. We have demonstrated how to call multiple functions in the same place. At least we know that there is such a method, but this is not completed during the compilation period ). We have enabled "signal sending" to be monitored by several different "slots.
However, our system is really nothing different. Let's review our system carefully. What's really different is:
- Two different terms are defined: "signal" and "slot ";
- Signal at one call point) connected to zero or multiple callback slots;
- The focus of the connection is removed from the provider and more to the consumer. That is to say, the Button does not need to know how to do it correctly. Instead, the callback function informs the Button that you need to call me ).
However, such a system is far from loosely coupled. The Button class does not need to know the Page class. Loose coupling means less dependencies. The less dependencies, the higher the reusability of components.
Of course, components must know both the Button and Page to connect them. Now, our connection is actually described by code. What if we use data to describe the connection without code? In this way, we have loosely coupled classes to improve the reusability of the two.
New Connection Mode
What connection mode is not a code description? If there is only one signature for the signal slot, such as void (* signature) (), this cannot be achieved. Use a hash to map the signal name to the matched connection function, map the slot name to the matched function pointer, and then create a connection with a pair of strings.
However, this implementation actually includes some "handshake" protocols. We do want signatures with multiple signal slots. In the short answer to the signal slot, we mentioned that signals can carry additional information. This requires that the signal has parameters. We have not processed the differences between member functions and non-member functions. This is another potential function signature difference. We have not decided whether to directly connect the signal to the slot function or to a package. If it is a package, the package already exists, or should we create it automatically when needed? Although the underlying idea is very simple, the real implementation still requires great efforts. It seems that using a class name to create objects is a good idea. It depends on your implementation method, and sometimes it even depends on whether you have the ability to make such an implementation. A registration mechanism is required to place signals and slots into a hash. Once such a system exists, the problem "too many classes" mentioned above can be solved. All you need to do is maintain the hash key value and instantiate the class as needed.
Adding such capabilities to the signal slot is much more difficult than all the work we have done before. When key-value connections are performed, most implementations will choose to discard the compilation period type security check to ensure compatibility between signals and slots. Such a system is more expensive, but its application is far higher than that of Automatic Signal slot connections. Such a system allows instantiation of external classes, such as buttons and their connections. Therefore, such a system has powerful capabilities. It can complete the Assembly and connection of a class, and finally complete the instantiation operation, such as a dialog box exported directly from the Resource Description file. Since it can make the function available by name, this is a script capability. If you need the features mentioned above, it is absolutely worthwhile to complete such a system, and your signal slot system will also benefit from the benefits of using data to connect the signal slot.
This feature is ignored for implementations that do not require this capability. From this point of view, this implementation is "lightweight. For a library that requires these features, it is fully implementedYesA lightweight implementation. This is also one of the methods to differentiate these implementations.
This article is from the "bean space" blog, please be sure to keep this source http://devbean.blog.51cto.com/448512/424778