我现在的回调方式是用boost::function,它在TR1时已经进入 C++ 标准库。Boost::function不对类型和函数名做限制,只对参数和返回类型做部分限制。如果你通过传统的继承来回调的话,你这个类型必须是framework里某个基类的派生类,函数的名字必须一样,参数列表必须一样,返回类型也基本肯定是一样。但是boost::function没有这些限制。Muduo网络库不是一个面向对象(object-oriented)的库,它是一个基于对象(object-based)的库。它在接口上没有表现出继承的特性,它用的是boost function的注册/回调机制,网络事件的表示就用 boost function。所以对Muduo来讲,它不需要知道你写什么类,也不强迫继承,更不需要知道你的函数叫什么名字,你给它的就是一个 boost function对象,限制就很少。而且你没有把对象指针传给网络库,那么就可以按原有的方式管理对象的生命期。”
--------------陈硕,开源社区访谈实录
面向对象的三大特点(封装,继承,多态)缺一不可。通常“基于对象”是使用对象,但是无法利用现有的对象模板产生新的对象类型,继而产生新的对象,也就是说“基于对象”没有继承的特点。而“多态”表示为父类类型的子类对象实例,没有了继承的概念也就无从谈论“多态”。现在的很多流行技术都是基于对象的,它们使用一些封装好的对象,调用对象的方法,设置对象的属性。但是它们无法让程序员派生新对象类型。他们只能使用现有对象的方法和属性。所以当你判断一个新的技术是否是面向对象的时候,通常可以使用后两个特性来加以判断。“面向对象”和“基于对象”都实现了“封装”的概念,但是面向对象实现了“继承和多态”,而“基于对象”没有实现这些。
本文通过实现Tread类来比较两种编程风格的差别。
(一)面向对象编程风格
Tread类图
Thread.h
#ifndef _THREAD_H_ #define _THREAD_H_ #include <pthread.h> class Thread { public: Thread(); virtual ~Thread(); void Start(); void Join(); void SetAutoDelete(bool autoDelete); private: static void *ThreadRoutine(void *arg); //没有隐含的this 指针 virtual void Run() = 0; pthread_t threadId_; bool autoDelete_; }; #endif // _THREAD_H_Tread.cpp
#include "Thread.h" #include <iostream> 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<Thread *>(arg); thread->Run(); //线程结束,线程对象也得析构 if (thread->autoDelete_) delete thread; return NULL; } void Thread::SetAutoDelete(bool autoDelete) { autoDelete_ = autoDelete; }Thread_test.cpp:
#include "Thread.h" #include <unistd.h> #include <iostream> 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; }注意点及其分析:
(1) 基类的析构函数不用virtual会发生什么?
可能会产生内存泄露。如果在派生类中申请了内存空间,并在其析构函数中释放,如果此时基类的析构函数不是virtual,就不会触发动态绑定,因而只会调用基类的析构函数,从而导致派生类对象的一些内存空间没有被释放,导致内存泄露。
(2) Thread类中 virtual void Run()=0 是纯虚函数,含有纯虚函数的类是抽象类,不能生成对象,所以要继承此基类并实现Run();
(3) 线程结束和线程对象的销毁是不同的概念,线程对象的生命周期要等到作用域结束。如果想要实现线程执行完毕,线程对象自动销毁,就需要动态创建对象,使用 delete 在 run 之后就将线程对象 delete 销毁掉。(4)根据 pthread_create 的原型
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
start_routine 参数是一般的函数指针,故不能直接将run() 作为此参数,因为run()是成员函数,隐含this指针,故实现一个静态成员函数ThreadRoutine(), 在里面调用run(),此外参数arg 我们传递this指针,在ThreadRoutine()内将派生类指针转换为基类指针来调用run()。
(5) 把run()实现为private是为了不让用户直接调用,因为这样根本就没有产生线程调度。函数适配器 boost bind/function用来实现转换函数接口。
#include <iostream> #include <boost/function.hpp> #include <boost/bind.hpp> using namespace std; class Foo { public: void memberFunc(double d, int i, int j) { cout << d << endl;//打印0.5 cout << i << endl;//打印100 cout << j << endl;//打印10 } }; int main() { Foo foo; boost::function<void (int)> fp = boost::bind(&Foo::memberFunc, &foo, 0.5, _1, 10); fp(100); boost::function<void (int, int)> fp2 = boost::bind(&Foo::memberFunc, &foo, 0.5, _1, _2); fp2(100, 200); boost::function<void (int, int)> fp3 = boost::bind(&Foo::memberFunc, boost::ref(foo), 0.5, _1, _2); fp3(55, 66); return 0; }
fp(100); 等价于 (&foo)->memberFunc(0.5, 100, 10); 即_1 是占位符,如果绑定的是一般的函数,则bind 中的参数中不再需要this指针,当然一般函数也没有类名前缀。成员函数的取地址符号不可以省略。
boost::ref() 表示引用,fp3(55, 66); 相当于foo.memberFunc(0.5, 55, 66);
typedef boost::function<void ()> ThreadFunc;
#ifndef _THREAD_H_ #define _THREAD_H_ #include <pthread.h> #include <boost/function.hpp> class Thread { public: typedef boost::function<void ()> 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 <iostream> 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<Thread *>(arg); thread->Run(); if (thread->autoDelete_) delete thread; return NULL; } void Thread::SetAutoDelete(bool autoDelete) { autoDelete_ = autoDelete; } void Thread::Run() { func_(); }
#include "Thread.h" #include <boost/bind.hpp> #include <unistd.h> #include <iostream> 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; }
class EchoServer { public: EchoServer() { server_.SetConnectionCallback(boost::bind(&EchoServer::OnConnection, ...)); ... } void OnConnection() { .. } TcpServer server_; };