我現在的回調方式是用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_ #includeTread.cppclass 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_
#include "Thread.h" #includeThread_test.cpp: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(); //線程結束,線程對象也得析構 if (thread->autoDelete_) delete thread; return NULL; } void Thread::SetAutoDelete(bool autoDelete) { autoDelete_ = autoDelete; }
#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; }
(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#include #include 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 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); 等價於 (&foo)->memberFunc(0.5, 100, 10); 即_1 是占位符,如果綁定的是一般的函數,則bind 中的參數中不再需要this指針,當然一般函數也沒有類名前綴。成員函數的取地址符號不可以省略。
boost::ref() 表示引用,fp3(55, 66); 相當於foo.memberFunc(0.5, 55, 66);
typedef boost::function
#ifndef _THREAD_H_ #define _THREAD_H_ #includeTread.cpp#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_
#include "Thread.h" #includeusing 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_(); }
#include "Thread.h" #include注意:Thread類不再是虛基類,run() 也不是純虛函數,Thread 有個成員ThreadFunc func_,此時不再是通過繼承基類來重新實現run(),進而實現多態;而是通過綁定不同的函數指針到func_ 上來實現不同的行為。我們既可以綁定一般的全局函數,也可以綁定其他類裡面的成員函數,操作很方便。此外,Thread t3, t4 不能綁定到同一個類對象foo 上,因為此時MemFun() 和MemFun2() 都會去訪問同一個對象foo的count_ ,就會出現問題了。#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; }
class EchoServer { public: EchoServer() { server_.SetConnectionCallback(boost::bind(&EchoServer::OnConnection, ...)); ... } void OnConnection() { .. } TcpServer server_; };