Object-Oriented programming style & amp; based on object programming (boost: bind/function)

Source: Internet
Author: User

Object-Oriented programming style & Object-based programming (boost: bind/function)
"Muduo is a modern C ++ network library. There are two differences between APIs in modern and ancient times. One is Event Callback, and the other is resource management. An API is designed for a network library to define an interface (abstract base class) that contains the processing functions corresponding to several network events. Your code inherits this interface, which defines which virtual function is called back when a message is received, and overwrites this virtual function. Register your object to the network library, and call back your virtual function when an event occurs. This is the practice of traditional or ancient C ++ Network Libraries and Java Network Libraries. One direct problem facing C ++ is the object lifecycle management, because the dynamic binding of C ++ can only be achieved through pointers and references, you must pass the base class pointer to the framework to get the Event Callback. Then, when the object in the derived class is destroyed, it becomes difficult. Who is the ownership of the object? Some Network Libraries even show the delete this in the event processing function. this Code makes people feel overwhelmed.

My current callback method is to use boost: function, which has entered the C ++ standard library at TR1. Boost: The function does not impose restrictions on types and function names, but only partially limits parameters and return types. If you call back and forth through traditional inheritance, you must be a derived class of a base class in the framework. The function name must be the same, and the parameter list must be the same, the return type is also basically the same. However, boost: function does not have these restrictions. The Muduo network library is not an object-oriented library. It is an object-based library. It does not show inherited features on the interface. It uses the registration/callback mechanism of boost function, and the expression of network events uses boost function. Therefore, for Muduo, it does not need to know what class you write or force inheritance, nor do it need to know the name of your function. What you give it is a boost function object, there are few restrictions. In addition, if you do not pass the object pointer to the network library, you can manage the object life cycle in the original way ."

-------------- Chen Shuo, open source community interview transcript

The three characteristics of object-oriented (encapsulation, inheritance, and polymorphism) are indispensable. Objects are usually used based on objects, but existing Object Templates cannot be used to generate new object types and generate new objects. That is to say, objects are not inherited. "Polymorphism" indicates a subclass object instance of the parent class. Without the concept of inheritance, there is no way to talk about "polymorphism ". Nowadays, many popular technologies are based on objects. They use encapsulated objects, call object methods, and set object attributes. However, they cannot allow programmers to derive new object types. They can only use methods and attributes of existing objects. So when you determine whether a new technology is object-oriented, you can usually use the last two features to determine. Both "Object-Oriented" and "Object-based" implement the concept of "encapsulation", but object-oriented implements "inheritance and polymorphism", while "Object-based" does not.

This article compares the differences between the two programming styles by implementing the Tread class.

(1) object-oriented programming style

Tread class diagram

Thread. h

 

# Ifndef _ THREAD_H _ # define _ THREAD_H _ # include
 
  
Class Thread {public: Thread (); virtual ~ Thread (); void Start (); void Join (); void SetAutoDelete (bool autoDelete); private: static void * ThreadRoutine (void * arg ); // No implied this pointer virtual void Run () = 0; pthread_t threadId _; bool autoDelete _ ;};# endif/_ THREAD_H _
 
Tread. cpp

 

 

# Include "Thread. h" # include
 
  
Using namespace std; Thread: Thread (): autoDelete _ (false) {cout <"Thread..." <endl;} Thread ::~ Thread () {cout <"~ Thread... "<endl;} void Thread: Start () {pthread_create (& threadId _, NULL, ThreadRoutine, this);} void Thread: Join () {pthread_join (threadId _, NULL);} void * Thread: ThreadRoutine (void * arg) {Thread * thread = static_cast
  
   
(Arg); thread-> Run (); // when the thread ends, the thread object also needs to be parsed if (Thread-> autoDelete _) delete thread; return NULL;} void thread:: SetAutoDelete (bool autoDelete) {autoDelete _ = autoDelete ;}
  
 
Thread_test.cpp:
#include "Thread.h"#include 
 
  #include 
  
   using namespace std;class TestThread : public Thread{public:    TestThread(int count) : count_(count)    {        cout << "TestThread ..." << endl;    }    ~TestThread()    {        cout << "~TestThread ..." << endl;    }private:    void Run()    {        while (count_--)        {            cout << "this is a test ..." << endl;            sleep(1);        }    }    int count_;};int main(void){    TestThread *t2 = new TestThread(5);    t2->SetAutoDelete(true);    t2->Start();    t2->Join();    for (; ; )        pause();    return 0;}
  
 
Note:

 

(1) What will happen if the destructor of the base class does not need to be virtual?

Memory leakage may occur. If you have applied for memory space in the derived class and released it in its destructor, if the destructor of the base class is not virtual at this time, dynamic binding will not be triggered, therefore, only the destructor of the base class are called. As a result, some memory spaces of the derived class objects are not released, resulting in Memory leakage.

(2) In the Thread class, virtual void Run () = 0 is a pure virtual function. Classes containing pure virtual functions are abstract classes and cannot generate objects, therefore, you must inherit this base class and implement Run ();

(3) The thread end and the destruction of the thread object are different concepts. The life cycle of the thread object must wait until the scope ends. If you want to achieve automatic destruction of thread objects after thread execution is completed, you need to dynamically create an object and delete the thread object after run.

(4) based on the pthread_create prototype

Int pthread_create (pthread_t * thread, const pthread_attr_t * attr,

Void * (* start_routine) (void *), void * arg );

The start_routine parameter is a common function pointer, so you cannot directly use run () as this parameter, because run () is a member function and implies this pointer, therefore, a static member function ThreadRoutine () is implemented, in which run () is called. In addition, the parameter arg is passed as the this pointer, in ThreadRoutine () to call run ().

(5) Implementing run () as private is to prevent users from calling it directly, because there is no thread scheduling at all.
(2) object-based programming style

Function adapter boost bind/function is used to implement the conversion function interface.

# Include
 
  
# Include
  
   
# Include
   
    
Using namespace std; class Foo {public: void memberFunc (double d, int I, int j) {cout <d <endl; // print 0.5 cout <I <endl; // print 100 cout <j <endl; // print 10 }}; int main () {Foo foo; boost: function
    
     
Fp = boost: bind (& Foo: memberFunc, & foo, 0.5, _ 1, 10); fp (100); boost: function
     
      
Fp2 = boost: bind (& Foo: memberFunc, & foo, 0.5, _ 1, _ 2); fp2 (100,200); boost: function
      
        Fp3 = boost: bind (& Foo: memberFunc, boost: ref (foo), 0.5, _ 1, _ 2); fp3 (55, 66 ); return 0 ;}
      
     
    
   
  
 

Fp (100); equivalent to (& foo)-> memberFunc (0.5, 100, 10); that is, _ 1 is a placeholder. If it is bound to a general function, the this pointer is no longer required for the parameters in the bind. Of course, the function generally does not have a class name prefix. The bitwise symbols of member functions cannot be omitted.

Boost: ref () indicates reference, fp3 (55, 66); equivalent to foo. memberFunc (0.5, 55, 66 );

Typedef boost: function ThreadFunc;

Thread. h
#ifndef _THREAD_H_#define _THREAD_H_#include 
 
  #include 
  
   class Thread{public:    typedef boost::function
   
     ThreadFunc;    explicit Thread(const ThreadFunc &func);    void Start();    void Join();    void SetAutoDelete(bool autoDelete);private:    static void *ThreadRoutine(void *arg);    void Run();    ThreadFunc func_;    pthread_t threadId_;    bool autoDelete_;};#endif // _THREAD_H_
   
  
 
Tread. cpp

 

 

#include "Thread.h"#include 
 
  using namespace std;Thread::Thread(const ThreadFunc &func) : func_(func), autoDelete_(false){}void Thread::Start(){    pthread_create(&threadId_, NULL, ThreadRoutine, this);}void Thread::Join(){    pthread_join(threadId_, NULL);}void *Thread::ThreadRoutine(void *arg){    Thread *thread = static_cast
  
   (arg);    thread->Run();    if (thread->autoDelete_)        delete thread;    return NULL;}void Thread::SetAutoDelete(bool autoDelete){    autoDelete_ = autoDelete;}void Thread::Run(){    func_();}
  
 

Thread_test.cpp:
#include "Thread.h"#include 
 
  #include 
  
   #include 
   
    using namespace std;class Foo{public:    Foo(int count) : count_(count)    {    }    void MemberFun()    {        while (count_--)        {            cout << "this is a test ..." << endl;            sleep(1);        }    }    void MemberFun2(int x)    {        while (count_--)        {            cout << "x=" << x << " this is a test2 ..." << endl;            sleep(1);        }    }    int count_;};void ThreadFunc(){    cout << "ThreadFunc ..." << endl;}void ThreadFunc2(int count){    while (count--)    {        cout << "ThreadFunc2 ..." << endl;        sleep(1);    }}int main(void){    Thread t1(ThreadFunc);    Thread t2(boost::bind(ThreadFunc2, 3));    Foo foo(3);    Thread t3(boost::bind(&Foo::MemberFun, &foo));    Foo foo2(3);    Thread t4(boost::bind(&Foo::MemberFun2, &foo2, 1000));    t1.Start();    t2.Start();    t3.Start();    t4.Start();    t1.Join();    t2.Join();    t3.Join();    t4.Join();    return 0;}
   
  
 
Note: The Thread class is no longer a virtual base class, and run () is not a pure virtual function. The Thread has a member ThreadFunc func _, instead of re-implementing run () by inheriting the base class, and then implementing polymorphism, You can bind different function pointers to func _ to implement different behaviors. We can bind general global functions or member functions in other classes for convenient operations. In addition, Thread t3 and t4 cannot be bound to the same class Object foo, because MemFun () and MemFun2 () both access the count _ of the same object foo _, the problem may occur.
Suppose that TcpServer is a network library, how can we use it? It depends on how it is implemented: (1) C programming style: register three global functions to the network library. The network library function parameters include the function pointer type, it is called back and forth through the function pointer. (2) Object-oriented style: An EchoServer is inherited from the TcpServer (abstract class) to implement three pure virtual function interfaces OnConnection, OnMessage, and OnClose. Use a base class pointer to call a virtual function to realize polymorphism. (3) object-based Style: use an EchoServer to include a TcpServer (specific class) object Member server, and use boost: bind in the constructor to register three member functions, such as server. setConnectionCallback (boost: bind (& EchoServer: OnConnection ,...)); that is, the server is set. connectionCallback _ Member, which calls server by binding different function pointers. connectionCallback _ () achieves different behaviors. As shown below.
class EchoServer{public:    EchoServer()    {        server_.SetConnectionCallback(boost::bind(&EchoServer::OnConnection, ...));                                      ...    }    void OnConnection()    {        ..    }    TcpServer server_;};

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.