This is a more emotional blog, the central idea is " inheritance is like a shanghaied, go up to get down ", and with the help of Boost::function and Boost::bind, in most cases, you do not have to shanghaied.
Boost::function and Boost::bind have been included in the STD::TR1, which may be the most anticipated feature of c++0x, which will revolutionize the way the C + + library is designed and how the application is written.
Scott Meyers's effective C + + 3rd ed. 35th mentions the substitution of virtual functions with boost::function and boost:bind, here's how I feel about my own use.
Original link: http://blog.csdn.net/Solstice/article/details/3066268
Replacing virtual functions with boost::function and Boost:bind
This is a more emotional blog, the central idea is " inheritance is like a shanghaied, go up to get down ", and with the help of Boost::function and Boost::bind, in most cases, you do not have to shanghaied.
Boost::function and Boost::bind have been included in the STD::TR1, which may be the most anticipated feature of c++0x, which will revolutionize the way the C + + library is designed and how the application is written.
Scott Meyers's effective C + + 3rd ed. 35th mentions the substitution of virtual functions with boost::function and boost:bind, here's how I feel about my own use.
Basic use
Boost::function, like the delegate in C #, can point to any function, including member functions. When bind binds a member function to an object, we get a closure (closure). For example:
Class Foo
{public
:
void MethodA ();
void Methodint (int a);
};
Class Bar
{public
:
void MethodB ();
};
Boost::function<void () > F1; No parameters, no return value
foo foo;
F1 = Boost::bind (&foo::methoda, &foo);
F1 (); Call Foo.methoda ();
Bar Bar;
F1 = Boost::bind (&bar::methodb, &bar);
F1 (); Call Bar.methodb ();
F1 = Boost::bind (&foo::methodint, &foo,);
F1 (); Call Foo.methodint (n);
boost::function<void (int) > F2; int parameter, no return value
F2 = Boost::bind (&foo::methodint, &foo, _1);
F2 (53); Call Foo.methodint (53);
If there is no boost::bind, then boost::function is nothing, and bind (), "different objects of the same class can delegate to different implementations to achieve different behaviors" (Myan), it is invincible.
The impact on the program library
The design of library is should not impose unnecessary constraints on the user (coupling), and inheritance is the second most powerful coupling (the strongest coupling is the friend). If a library restricts its users from having to derive from a class, then I think it's a bad design. Unfortunately, there are some libraries that do so at the moment.
Example 1: Line threading
General oo Design:
Write a Thread base class that contains (pure) virtual functions Thread#run (), and then the application derives an inherited class that is overridden by run (). Each thread in the program corresponds to a derived class of thread. For example, Java thread can be used in this way.
Disadvantage: If the three method of a class needs to be executed in three different threads, write helper Class (es) and play some oo tricks.
based on the closure design:
The thread is a concrete class whose constructor accepts the callable object. The application simply provides a callable object, creates a thread entity, and invokes Thread#start (). Java thread can also be used so that a Runnable object is passed in. Thread in C # only supports this usage, and the constructor parameter is delegate ThreadStart. Boost::thread also supports this use only.
A thread class basic structure based on closure
class thread
{public
:
typedef boost::function<void () > Threadcallback;
Thread (Threadcallback CB): Cb_ (CB)
{}
void start ()
{/
* Some magic to call run () in new created thread * /
}
private:
void Run ()
{
cb_ ()
}
Threadcallback cb_;
// ...
Use:
Class Foo
{public
:
void Runinthread ();
};
Foo foo;
Thread Thread (Boost::bind (&foo::runinthread, &foo));
Thread.Start ();
Example 2: Network library
With Boost::function as a bridge, NetServer class does not have any type restrictions on its users, only restrictions on the parameters and return types of member functions. The user echoservice also completely do not know the existence of NetServer, as long as in main () to assemble the two together, the program ran.
Library class Connection;
Class Netserver:boost::noncopyable {public:typedef boost::function<void (connection*) > ConnectionCallback;
typedef boost::function<void (connection*, const void*, int len) > messagecallback;
NetServer (uint16_t port);
~netserver ();
void Registerconnectioncallback (const connectioncallback&);
void Registermessagecallback (const messagecallback&);
void SendMessage (connection*, const void* buf, int len);
Private://...}; User class EchoService {public:typedef boost::function<void (connection*, const void*, int) > SENDMESSAGECALLB Ack
Netserver::sendmessage-compliant prototype echoservice (const sendmessagecallback& SENDMSGCB): Sendmessagecb_ (SENDMSGCB) {} void OnMessage (connection* conn, const void* buf, int size)//conforming to Netserver::netserver::messagecallback prototype {p
rintf ("Received Msg from Connection%d:%.*s/n", Conn->id (), size, (const char*) BUF); SENDMESSAGECB_ (conn, buf, size); Echo Back} void OnConnection (connection* conn)//conforming to Netserver::netserver::connectioncallback prototype {printf ("Connection From%s:%d is%s/n, conn->ipaddr (), Conn->port (), conn->connected ()?
"Up": "Down");
} Private:sendmessagecallback sendmessagecb_;
};
Play the role of God, put the pieces together int main () {NetServer server (7);
EchoService Echo (Bind (&netserver::sendmessage, &server, _1, _2, _3));
Server.registermessagecallback (Bind (&echoservice::onmessage, &echo, _1, _2, _3));
Server.registerconnectioncallback (Bind (&echoservice::onconnection, &echo, _1));
Server.run ();
}
The influence of object-oriented programming
All along, I have a sense of disgust to the object-oriented, duplication, around, a sincere dozen on cotton, not solve practical problems. Object-oriented three elements are encapsulation, inheritance and polymorphism. I think encapsulation is fundamental, and inheritance and polymorphism are dispensable. Using class to express concept, this is fundamental; As for inheritance and polymorphism, the coupling is too strong and often not cost-effective.
Inheritance and polymorphism not only specify the function name, parameter, return type, but also the inheritance relationship of the class. In modern OO programming languages, the limitations have been greatly eased by reflection and attribute/annotation. For example, the JUnit 3.x is performed using reflection to find a function in the derived class whose name is in accordance with void test* (), where nothing is inherited, but there is a partial restriction on the name of the function (inheritance is a complete limitation, the word is not bad). As for JUnit 4.x and NUnit 2.x, it goes a step further, with Annoatation/attribute to mark the test case, much less inherited.
My guess is that when we first presented the object-oriented, closure didn't have a universal implementation, so it didn't count as one of the basic abstract tools. Now that closure has been so convenient, perhaps we should re-examine object-oriented design, at least not so abusive inheritance.
Since found Boost::function+boost::bind this to the divine weapon, no longer consider the kind of direct inheritance relationship, only need to be based on the object of the design (object-based), sincere to the meat, the program is written suddenly a lot of convenience.
Impact on object-oriented design patterns
Since virtual functions can be replaced with closure, many oo design patterns, especially behavioral patterns, have lost the need for existence. In addition, since there is no inheritance system, then the creation of the pattern does not seem to have much use.
The most obvious is strategy, without the cumbersome strategy base class and Concretestrategya, Concretestrategyb and other derived classes, a boost::function<> member to solve the problem. In "design mode" this book mentions 23 patterns, I think iterator useful (perhaps add a state), the other is in keep up appearances, pull the empty shelf, no use. Maybe they solve the common problems in object-oriented, but if my program doesn't use object-oriented (inheritance and polymorphism), it doesn't seem to be nagging object-oriented design patterns.
Perhaps closure-based programming will become popular as a new programming paradiam.
Dependency Injection and Unit testing
The previous echoservice can be counted as an example of dependency injection, echoservice need something to send a message, and its requirement for this "thing" is just a function prototype to satisfy the sendmessagecallback, not whether the data is sent to the network or to the console. In normal use, the data should be sent to the network, and in the unit test, the data should be sent to a datasink.
An object-oriented idea, first write a abstractdatasink interface, including SendMessage () This virtual function, and then derive two Classes:netdatasink and Mockdatasink, the front of the work, The unit test in the back. The EchoService constructor should take abstractdatasink* as the parameter, thus realizing the so-called interface and implementation separation.
I think this is simply to take off the pants fart, directly into a Sendmessagecallback object can solve the problem. In unit testing, you can Boost::bind () to Mockserver, or on a global function, without inheriting and virtual functions, and without affecting existing designs.
When to use inheritance.
If you refer to the public inheritance in Oo, that is, for the interface to be detached from the implementation, I will only use the number and functionality of the derived classes when they are fully determined. In other words, if you don't think about future expansion, object-oriented may be a good way to describe it. Once you want to consider the expansion, what method is useless, or better to write a simple point of the program, the future of a great change or rewrite.
If it is a feature inheritance, then I will consider inheriting boost::noncopyable or boost::enable_shared_from_this, the next blog will talk about Enable_shared_from_ This is the magic of Signal/slot when implementing multithreading security.
For example, Io-multiplex has different recommended implementations under different operating systems, the most versatile select (), POSIX poll (), Linux epoll (), FreeBSD kqueue, and so on, with fixed numbers and fully defined functions, regardless of extension. Then design a netloop base class plus a number of specific classes is a good solution.
interface-based design
The question comes from the classic discussion that the flightless penguin (Penguin) should not inherit from the bird (Bird) If Bird defines the virtual function fly (). The result of the discussion is that the specific actions are presented as interface, such as flyable (able to fly), Runnable (able to run), and then let the Penguins achieve Runnable, the Sparrows realize flyable and Runnable. (In fact, the sparrow can only jump feet, can not run, here do not delve.) )
Further discussion shows that the granularity of the interface should be small enough, perhaps containing a method, and then the interface actually degenerate into a label for the type. In this case, boost::function can be used instead, such as:
Penguins can swim, they can run
class Penguin
{public
:
void Run ();
void swim ();
The sparrow can fly, also can run
class Sparrow
{public
:
void Fly ();
void run ();
With closure as interface
typedef boost::function<void () > Flycallback;
typedef boost::function<void () > Runcallback;
typedef boost::function<void () > Swimcallback;
A client class
class Foo
{public
:
foo (flycallback FLYCB, Runcallback RUNCB) that uses both run and fly: Flycb_ (FLYCB), Runcb_ (RUNCB)
{}
private:
flycallback flycb_;
Runcallback runcb_;
};
A customer class
class Bar
{public
:
Bar (Swimcallback SWIMCB, Runcallback RUNCB) that uses both run and swim: Swimcb_ (SWIMCB), Runcb_ (RUNCB)
{}
private:
swimcallback swimcb_;
Runcallback runcb_;
};
int main ()
{
Sparrow s;
Penguin p;
Assemble, Foo wants Sparrow, Bar wants Penguin.
foo foo (bind (&sparrow::fly, &s), bind (&sparrow::run, &s));
Bar bar (Bind (&penguin::swim, &p), bind (&penguin::run, &p));
Implement Signal/slot
Boost::function + Boost::bind Describes a one-to-one callback, in which we use BOOST::SHARED_PTR + boost::weak_ptr to achieve a simple multicast (multi-cast), a One-to-many callback, And consider the life-time management of objects and multithreading security, and, naturally, the type of users without any restrictions, a little longer, stay for the next blog bar. (Boost::signals also implemented Signal/slot, but unfortunately not thread-safe.) )
Finally, pay tribute to the great C language.