欧美三区_成人在线免费观看视频_欧美极品少妇xxxxⅹ免费视频_a级毛片免费播放_鲁一鲁中文字幕久久_亚洲一级特黄

boost源碼剖析之:多重回調機制signal(下)

系統 1947 0

boost 源碼剖析之:多重回調機制 signal( )

劉未鵬

C++ 的羅浮宮 (http://blog.csdn.net/pongba)

本文的上篇 中,我們大刀闊斧的剖析了 signal 的架構。不過還有很多精微之處沒有提到,特別是一個遺留問題還沒有解決:如果用戶注冊的是函數對象(仿函數), signal 又當如何處理呢?

下篇:高級篇

概述

在本文的上篇中,我們已經分析了 signal 的總體架構。至于本篇,我們則主要集中于將 函數對象 (即仿函數)連接到 signal 的來龍去脈。 signal 庫的作者在這個方面下了很多功夫,甚至可以說,并不比構建整個 signal 架構的功夫下得少。

之所以為架構,其中必然隱藏著一些或重要或精妙的思想。

學過 STL 的人都知道,函數對象 [1] (function object) STL 中的重要概念和基石之一。它使得一個對象可以像函數一樣被 調用 ,而調用形式又是與函數一致的。這種一致性在泛型編程中乃是非常重要的,它意味著 泛化 ,而這正是泛型世界所有一切的基礎。而函數對象又由于其攜帶的信息較之普通函數大為豐富,從而具有更為強大的能力。

所以 signal 簡直是 不得不 支持函數對象。然而函數對象又和普通函數不同:函數對象會析構。問題在于:如果某個函數對象連接到 signal ,那么,該函數對象析構時,連接是否應該斷開呢?這個問題, signal 的設計者留給用戶來選擇:如果用戶覺得函數對象一旦析構,相應的連接也應該自動斷開,則可以將其函數對象派生自 boost::signals::trackable 類,意即該對象是 可跟蹤 的。反之則不用作此派生。這種跟蹤對象析構的能力是很有用的,在某些情況下,用戶需要這種語義:例如,一個負責數據庫訪問及更新的函數對象,而該對象的生命期受某個管理器的管理,現在,將它連接到某個代表用戶界面變化的 signal ,那么,當該對象的生命期結束時,對應的連接顯然應該斷開 —— 因為該對象的析構意味著對應的數據庫不再需要更新了。

signal 庫支持跟蹤函數對象析構的方式很簡單,只要將被跟蹤的函數對象派生自 boost::signals::trackable 類即可,不需要任何額外的步驟。解剖這個 trackable 類所隱藏的秘密正是本文的重點。

架構

很顯然, trackable 類是整個問題的關鍵。將函數對象派生自該類,就好比為函數對象安上了一個 跟蹤器 。根據 C++ 語言的規則,當某個對象析構時,先析構派生層次最高 (most derived) 的對象,再逐層往下析構其子對象。這就意味著,函數對象的析構最終將會導致其基類 trackable 子對象的析構,從而在后者的析構函數中,得到斷開連接的機會。那么,哪些連接該斷開呢?換句話說,該斷開與哪些 signal 的連接呢?當然是該函數對象連接到的 signals 。而這些連接則全部保存在一個 list 里面。下面就是 trackable 的代碼:

class trackable {

typedef std::list<connection> connection_list;

typedef connection_list::iterator connection_iterator;

mutable connection_list connected_signals ;

...

}

connected_signals 是個 list ,其中保存的是該函數對象所連接到的 signals 。只不過是以 connection 的形式來表示的。這些 connection 都是 控制性 [2] 的,一旦析構則自動斷開連接。所以, trackable 析構時根本不需要任何額外的動作,只要讓該 list 自行析構就行了。

了解了這一點,就可以畫出可跟蹤的函數對象的基本結構,如 圖四

圖四

<shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 204.75pt; HEIGHT: 233.25pt" type="#_x0000_t75"><imagedata o:title="boost" src="file:///C:/DOCUME~1/pongba/LOCALS~1/Temp/msohtml1/01/clip_image001.gif"></imagedata></shape>

boost源碼剖析之:多重回調機制signal(下)

現在的問題是,每當該函數對象連接到一個 signal ,都會將相應 connection 的一個副本插入到其 trackable 子對象的 connected_signals 成員 ( 一個 list) 中去。然而,這個插入究竟發生在何時何地呢?

在本文的上篇中曾經分析過連接的過程。對于函數對象,這個過程仍然是一樣。不過,當時略過了一些細節,這些細節正是與函數對象相關的?,F在一一道來:

如你所知,在將函數 ( 對象 ) 連接到 signal 時,函數 ( 對象 ) 會先被封裝成一個 slot 對象, slot 類的構造函數如下:

slot(const F& f):slot_function(get_invocable_slot(f,tag_type(f)))

{

// 一個 visitor ,用于訪問 f 中的每個 trackable 子對象

bound_objects_visitor do_bind( bound_objects );

// 如果 f 為函數對象,則訪問 f 中的每一個 trackable 子對象

visit_each (do_bind,get_inspectable_slot [3] (f,tag_type(f)));

// 創建一個 connection ,表示 f 與該 slot 的連接,這是為了實現 “delayed-connect”

create_connection();

}

bound_objects slot 類的成員,其類型為 vector<const trackable*> 。可想而知,經過第二行代碼 “visit_each(...)” 的調用,該 vector 中保存的將是指向 f 中的各個 trackable 子對象的指針。

等等! 你敏銳的發現了一個問題: 前面不是說過,如果用戶要讓他的函數對象成為可跟蹤的,則將該函數對象派生自 trackable 對象嗎?那么,也就是說,如果 f 是個 可跟蹤 的函數對象,那么其中的 trackable 子對象當然只有一個(基類對象)!但為什么這里 bound_objects 的類型卻是一個 vector 呢?單單一個 trackable* 不就夠了么?

在分析這個問題之前,我們先來看一段例子代碼:

struct S1:boost::signals::trackable

{// 該對象是可跟蹤的!但并非一個函數對象

void test(){cout<<"test/n";}

};

...

boost::signal< void ()> sig;

{ // 一個局部作用域

S1 s1;

sig.connect( boost::bind(&S1::test,boost::ref(s1)) );

sig(); // 輸出 “test”

} // 結束該作用域 ,s1 在此析構,斷開連接

sig(); // 無輸出

boost::bind() &S1::test [4] “this” 參數綁定為 s1 ,從而生成一個 “void()” 型的仿函數,每次調用該仿函數就相當于調用 s1.test() ,然而,這個仿函數本身并非可跟蹤的,不過,很顯然,這里的 s1 對象一旦析構,則該仿函數就失去了意義,從而應該讓連接斷開。所以,我們應該使 S1 類成為可跟蹤的(見 struct S1 的代碼)。

然而,這又能說明什么呢?仍然只有一個 trackable 子對象!但是,答案已經很明顯了:既然 boost::bind 可以綁定一個參數,難道不能綁定兩個參數?對于一個延遲調用的函數對象 [5] ,一旦其某個按引用語義傳遞的參數析構了,該函數對象也就相應失效了。所以,對于這種函數對象,其按引用傳遞的參數都應該是可跟蹤的。在上例中, s1 就是一個按引用傳遞的參數 [6] ,所以是可跟蹤的。所以,如果有多個這種參數綁定到一個仿函數,就會有多個 trackable 對象,其中任意一個對象的析構都會導致仿函數失效以及連接的斷開。

例如,假設 C1,C2 類都是 trackable 的。并且函數 test 的類型為 void(C1,C2) 。那么 boost::bind(&test,boost::ref(c1),boost::ref(c2)) 就會返回一個 void() 型的函數對象,其中 c1,c2 作為 test 的參數綁定到了該函數對象。這時候,如果 c1 c2 析構,這個函數對象也就失效了。如果先前該函數對象曾連接到某個 signal<void()> 型的 signal ,則連接應該斷開。

問題在于,如何獲得綁定到某個函數對象的所有 trackale 子對象呢?

關鍵在于 visit_each 函數 —— 我們回到 slot 的構造函數(見上文列出的源代碼),其第二行代碼調用了 visit_each 函數,該函數負責訪問 f 中的各個 trackable 子對象,并將它們的地址保存在 bound_objects 這個 vector 中。

至于 visit_each 是如何訪問 f 中的各個 trackable 子對象的,這并非本文的重點,我建議你自行參考源代碼。

slot 類的構造函數最后調用了 create_connection 函數,這個函數創建一個連接對象,表示函數對象和該 slot 的連接。 咦?為什么和 slot 連接,函數對象不是和 signal 連接的嗎? 沒錯。但這個看似蛇足的舉動其實是為了實現 “delayed connect” ,例如:

void delayed_connect(Functor* f)

{

// 構造一個 slot ,但暫時不連接

slot_type slot(*f);

// 使用 f 做一些事情,在這個過程中 f 可能會被析構掉

...

// 如果 f 已經被析構了,則 slot 變為 inactive 態,則下面的連接什么事也不做

sig.connect(slot);

}

...

Functor* pf= new Functor();

delayed_connect(pf);

...

這里,如果在 slot 連接到 sig 之前, f“ 不幸 析構了,則連接不會生效,只是返回一個空連接。

為了達到這個目的, slot 類的構造函數使用 create_connection 構造一個連接,這個連接其實沒有實際意義,只是用于 監視 函數對象是否析構。如果函數對象析構了,則該連接會變為 斷開 態。下面是 create_connection 的源代碼:

摘自 libs/signals/src/slot.cpp

void slot_base::create_connection()

{

basic_connection* con = new basic_connection();

con->signal = static_cast < void *>( this );

con->signal_data = 0;

con->signal_disconnect = &bound_object_destructed;

watch_bound_objects.reset(con);

...

}

這段代碼先 new 了一個連接,并將其三個成員設置妥當。由于該連接純粹僅作 監視 該函數對象是否析構之用,并非真的 連接 slot ,所以 signal_data 成員只需閑置為 0 ,而 signal_disconnect 所指的函數 &bound_object_destructed 也只不過是個什么事也不做的空函數。關鍵是最后一行代碼: watch_bound_objects 乃是 slot 類的成員,類型是 connection ,這行代碼使其指向上面新建的 con 連接對象。注意,在后面省略掉的部分代碼中, 該連接的副本也被保存到待連接的函數對象的各個 trackable 子對象中 (前面已經提到(參見圖四),這系保存在一個 list 中),這才真正使得 監視 成為可能!因為這樣做了之后,一旦代連接的函數對象析構了,將會導致 con 連接為 斷開 狀態。從而在 sig.connect(slot) 時可以通過查詢 slot 中的 watch_bound_objects 副本的連接狀態得知該 slot 是否有效,如果無效,則返回一個空的連接。這里, connection 巧妙的充當了一個 監視器 的作用。

說到這里,你應該也就明白了為什么 basic_connection signal signal_data 成員的類型為 void* 而不是 signal_base_impl* slot_iterator* —— 是的,因為函數對象不但連接到 signal ,還 連接 slot 。將這兩個成員類型設置為 void* 可以復用該類以使其充當 監視器 的角色。 signal 庫的作者真可謂惜墨如金。

回到正題,我們接著考察如何將封裝了函數對象的 slot 連接到 signal 。這里,我建議你先回顧本文的上篇,因為這與將普通函數連接到 signal 有很大一部分相同之處,只不過多做了一些額外的工作。

同樣,可想而知的是,這個連接過程仍然是先將 slot 插入到 signal 中的 slot 管理器中去,并將 signal 的地址,插入后指向該 slot 的迭代器的地址,以及負責斷開連接的函數地址分別保存到表示本次連接的 basic_connection 對象的三個成員 [7] 中去。這時,故事幾乎已經結束了一半 —— 用戶已經可以通過該對象來控制相應連接了。但是,注意,只是 用戶 !對于函數對象來說,不但用戶能夠控制連接,函數對象也必須能夠 控制 連接,因為它析構時必須能夠斷開連接,所以,我們還需要將該連接對象的副本保存到函數對象的各個 trackable 子對象中去:

摘自 libs/signals/src/signal_base.cpp

connection

signal_base_impl::

connect_slot(const any& slot,

const any& name,

const std::vector<const trackable*>& bound_objects)

{

... // 創建 basic_connection 對象并設置其成員

// 下面的 for 循環將該連接的副本保存到各個 trackable 子對象中

for (std::vector<const trackable*>::const_iterator i =

bound_objects.begin();

i != bound_objects.end();++i)

{

bound_object binding;

(*i)->signal_connected(slot_connection, binding );

con->bound_objects.push_back(binding);

}

...

}

在上面的代碼中, for 循環遍歷綁定到該函數對象的各個 trackable 子對象,并將該連接的副本 slot_connection 保存到其中。這樣,當某個 trackable 子對象析構時,就會通過保存在其中的副本來斷開該連接,從而達到 跟蹤 的目的。

但是,這里還有個問題:這里實際的連接只有一個,但卻產生了多個副本,分別操縱在各個 trackable 子對象手中,如果用戶愿意,用戶還可以操縱一個或多個副本。但是,一旦該連接斷開 —— 不管是由于某個 trackable 子對象的析構還是用戶手動斷開 —— 則保存在各個 trackable 子對象中的該連接的副本都應該被刪除掉。不然既占空間又沒有任何意義,還會導致這樣的情況:只要其中有一個 trackable 對象還沒有析構,表示該連接的 basic_connection 對象就不會被 delete 掉。特別是當連接由用戶斷開時,每個未析構的 trackable 對象中都會仍留有一個該連接對象的副本,直到 trackable 對象析構時該副本才會被刪除。這就意味著,如果存在一個 長命百歲 trackable 函數對象,并在其生命期中頻繁被用戶連接到 signal 并頻繁斷開連接,那么,每次連接都會遺留一個連接副本在其 trackable 基類子對象中,這是個巨大的累贅。

那么,這個問題到底如何解決呢? basic_connection 仍然是問題的核心,既然用戶只能通過 connection 對象來控制連接,而 connection 對象實際上完全通過 basic_connection 來操縱連接,那么如何解決這個問題的責任當然落在 basic_connection 身上 —— 既然它知道哪個函數(對象)連接到哪個 signal 并在其 slot 管理器中的位置,那么,為什么不能讓它也知道 該連接在各個 trackable 對象中的副本所在何處 呢?

當然可以。答案就在于 basic_connection 的第四個成員 bound_objects ,其定義如下:

std::list<bound_object> bound_objects;

該成員正是用來記錄 該連接在各個 trackable 對象中的副本所在何處 的。它的類型是 std::list ,其中每一個 bound_object 型的對象都代表 某一個連接副本所在之處 。有了它,在斷開連接時,就可以依次刪除各個 trackable 對象中的副本。

那么,這個 bound_objects 又是何時被填充的呢?當然是在連接時,因為只有在連接時才知道有幾個 trackable 對象,并有機會將副本保存到它們內部。我們回顧上文的 connect_slot 函數的代碼,其中有加底紋的部分剛才沒有分析,這正是與此相關的。為了清晰起見,我們將分析以源代碼注釋的形式寫出來:

//bound_object 對象保存的是連接副本在 trackable 對象中的位置

bound_object binding;

// 調用的是 trackable::signal_connected 函數,該函數告訴 trackable 對象它已經連接到了 signal ,并提供連接的副本(第一個參數),該函數會將該副本插入到 trackable 的成員 connected_signals (見篇首 trackable 類的代碼)中去。并將插入的位置反饋給 binding 對象(第二個參數,按引用傳遞),這時候,通過 binding 就能夠將該副本從 trackable 對象中刪除。

(*i)->signal_connected(slot_connection, binding );

// 將接受反饋后的 binding 對象保存到該連接的 bound_objects 成員中去,以便以后通過它來刪除連接的副本

con->bound_objects.push_back(binding);

要想完全搞清楚以上幾行代碼,我們還得來看看 bound_object 類的結構以及 trackable::signal_connected 到底干了些什么?先來看看 bound_object 的結構:

摘自 boost/signals/connection.hpp

struct bound_object {

void * obj;

void * data;

void (*disconnect)( void *, void *);

}

發現什么特別的沒有?是的,它的結構簡直就是 basic_connection 的翻版,只不過成員的名字不同了而已。 basic_connection 因為是控制連接的樞紐,所以其三個成員表現的是被連接的 slot signal 中的位置。而 bound_object 表現的是 connection 副本在 trackable 對象中的位置。在介紹 bound_object 的三個成員之前,我們先來考察 trackable::signal_connected 函數,因為這個函數同時也揭示了這三個成員的含義:

摘自 libs/signals/src/trackable.cpp

void trackable::signal_connected(connection c,

bound_object& binding )

{

// connection 副本插入到 trackable 對象中的 connected_signals 中去, connected_signals 是個 std::list<connection> 型的容器,負責跟蹤該對象連接到了哪些 signal (見篇首的詳述)。

connection_iterator pos =

connected_signals.insert(connected_signals.end(), c);

// 將該 trackable 對象中保存的 connection 副本設置為 控制性 的,從而該副本析構時才會自動斷開連接。

pos->set_controlling();

//obj 指針指向 trackable 對象,注意這里將 trackable* 轉型為 void* 以利于保存。

binding.obj = const_cast < void *>( reinterpret_cast < const void *>( this ));

//data 指向 connection 副本在 connected_signals 容器中的位置,注意這里的轉型

binding.data = reinterpret_cast < void *>( new connection_iterator(pos));

// 通過這個函數指針,可以將這個 connection 副本刪除: signal_disconnected 函數接受 obj data 為參數,將 connection 副本 erase

binding.disconnect = & signal_disconnected ;

}

分析完了這段代碼, bound_object 類的三個成員的含義不言自明。注意,其最后一個成員是個函數指針,指向 trackable::signal_disconnected 函數,這個函數負責將一個 connection 副本從某個 trackable 對象中刪除,其參數有二,正是 bound_object 的前兩個成員 obj data ,它們合起來指明了一個 connection 副本的位置。

當這些副本在各個 trackable 子對象中都安置妥當后,連接就算完成了。我們再來看看連接具體是如何斷開的,對于函數對象,斷開它與某個 signal 的連接的過程大致如下:首先,與普通函數一樣,將函數對象從 signal slot 管理器中 erase 掉,這個連接就算斷開了。其次就是只與函數對象相關的動作了:將保存在綁定到函數對象的各個 trackable 子對象中的 connection 副本清除掉。這就算完成了斷開 signal 與函數對象的連接的過程。當然,得看到代碼心里才踏實,下面就是:

void connection::disconnect()

{

if ( this ->connected()) {

shared_ptr<detail::basic_connection> local_con = con;

// 先將該函數指針保存下來

void (*signal_disconnect)( void *, void *) =

local_con->signal_disconnect;

// 然后再將該函數指針置為 0 ,表示該連接已斷開

local_con->signal_disconnect = 0;

// 斷開連接, signal_disconnect 函數指針指向 signal_base_impl::slot_disconnected 函數,該函數在本文的上篇已作了詳細介紹

signal_disconnect(local_con->signal, local_con->signal_data);

// 清除保存在各個 trackable 子對象中的 connection 副本

typedef std::list<bound_object>::iterator iterator;

for (iterator i = local_con->bound_objects.begin();

i != local_con->bound_objects.end(); ++i) {

// 通過 bound_object 的第三個成員, disconnect 函數指針來清除該連接的每個副本

i->disconnect(i->obj, i->data);

}

}

}

前面已經說過, bound_object 的第三個成員 disconnect 指向的函數為 trackable::signal_disconnected ,顧名思義, “signal” 已經 “disconnected” 了,該是清除那些多余的 connection 副本的時候了,所以,上面的最后一行代碼 “i->disconnect(...)” 就是調用該函數來做最后的清理工作的:

摘自 libs/signals/src/trackable.cpp

void trackable::signal_disconnected( void * obj, void * data)

{

// 將兩個參數轉型,還其本來面目

trackable* self = reinterpret_cast <trackable*>(obj);

connection_iterator* signal =

reinterpret_cast <connection_iterator*>(data);

if (!self->dying) {

// connection 副本 erase

self->connected_signals.erase(*signal);

}

delete signal;

}

這就是故事的全部。這個清理工作一完成,函數對象與 signal 就再無瓜葛,從此分道揚鑣?;剡^頭來再看看 signal 庫對函數對象所做的工作,可以發現,其主要圍繞著 trackable 類的成員 connected_signals basic_connection 的成員 bound_objects 而展開。這兩個一個負責保存 connection 的副本以作跟蹤之用,另一個則負責在斷開連接時清除 connection 的各個副本。

分析還屬其次,重要的是我們能夠從中汲取到一些納為己用的東西。關于 trackable 思想,不但可以用在 signal 中,在其它需要跟蹤對象析構語義的場合也大可用上。這種架構之最妙之處就在于用戶只要作一個簡單的派生,就獲得了完整的對象跟蹤能力,一切的一切都在背后嚴密的完成。

蛇足 & 再談調用

還記得在本文的上篇分析的 調用 部分嗎?庫的作者藉由一個所謂的 “slot_call_iterator” 來完成遍歷 slot 管理器和調用 slot 的雙重任務。 slot_call_iterator slot 管理器本身的 iterator 語義幾乎相同,只不過對前者解引用 (dereference ,即 “*iter”) 的背后其實調用了其指向的 slot 函數,并且返回的是 slot 函數的返回值。這種特殊的語義使得 signal 可以將 slot_call_iterator 直接交給用戶制定的返回策略(如 max_value<> , min_value<> 等),一石二鳥。但是這里面有一個難以察覺的漏洞:一個設計得不好的算法可能會使迭代器在相同的位置上出現冗余的解引用,例如,一個設計的不好的 max_value<> 可能會像這樣:

T max = *first++;

for (; first != last; ++first)

max = ( *first > max)? *first : max;

這個算法本身的邏輯并沒有什么不妥,只不過注意到其中 *first 出現了兩次,這意味著什么?如果按照以前的說法,每一次解引用都意味著一次函數調用的話,那么同一個函數將被調用兩次。這可就不合邏輯了。 signal 必須保證每個注冊的函數有且僅有一次執行的機會。

解決這個問題的任務落在庫的設計者身上,無論如何,一個普通用戶寫出上面的算法的確是件無可非議的事。一個明顯的解決方案是將函數的返回值緩存起來,第二次或第 N 次在同一位置解引用時只是從緩存中取值并返回。 signal 庫的設計者正是采用的這種方法,只不過, slot_call_iterator 將緩存的返回值交給一個 shared_ptr 來掌管。這是因為,用戶可能會拷貝迭代器,以暫時保存區間中的某個位置信息,在拷貝迭代器時,如果緩存中已經有返回值,即函數已經調用過了,則新的迭代器也因該引用那個緩存。并且,當最后一個引用該緩存的迭代器消失時,就是該緩存被釋放之時,這正是 shared_ptr 用武之地。具體的實現代碼請你自行參考 boost/signals/detail/slot_call_iterator.hpp 。

值得注意的是, slot_call_iterator 符合 “single pass” (單向遍歷) concept 。對于這種類型的迭代器只能進行兩種操作:遞增和比較。這就防止了用戶寫出不規矩的返回策略 —— 例如,二分查找(它要求一個隨機迭代器)。如果用戶硬要犯規,就會得到一個編譯錯誤。

由此可見,設計一個完備的庫不但需要技術,還要無比的細心。

結語

相對于 C++ 精致的泛型技術的應用來說,其背后隱藏的思想更為重要。在 signal 庫中,泛型技術的應用其實也不可不謂淋漓盡致,但是語言只是工具,重要的是解決問題的思想。從這篇文章可以看出,作者為了構建一個功能完備,健壯,某些特性可定制的 signal 架構付出了多少努力。雖然某些地方看似簡單,如 connection 對象,但是都是經過反復揣摩,時間檢驗后作出的設計抉擇。而對于函數對象,更是以一個 trackable 基類就實現了完備的跟蹤能力。以一個函數對象來定制返回策略則是符合 policy-based 設計的精髓。另外還有一些細致入微的設計細節,本篇并沒有一一分析,一是為了讓文章更緊湊,二是篇幅 —— 只講主要脈絡文章尚已如此,再加上各個細節則更是 了得 了,干脆留給你自行理解,你將 boost 的源代碼和本文列出的相應部分比較后或會發現一些不同之處,那些就是我故意省略掉的細節所在了。對于細節有興趣的不妨自己分析分析。

目錄 ( 展開 boost 源碼剖析》系列 文章 )



[1] 函數對象即重載了 operator() 操作符的對象,故而可以以與函數調用一致的語法形式來 調用 。又稱為 functor ,中文譯為 仿函數

[2] 控制性 是指該 connection 析構時會順便將該連接斷開。反之則不然。關于 控制性 非控制性 connection 的詳細討論見本文的上篇。

[3] get_inspectable_slot() 當且僅當 f 是個 reference_wrapper 時,返回 f.get()—— 即其中封裝的真實的函數(對象)。其它時候,該函數調用等同于 f 。關于 reference_wrapper 的詳細介紹見 boost 的官方文檔。

[4] &S1::test 為指向成員函數的指針。其調用形式為 (this_ptr->*mem_fun_ptr)() (this_ref.*mem_fun_ptr)() ,而從一般語義上說,其調用形式為 mem_fun_ptr(this_ref) mem_fun_ptr(this_ptr) 。所以, boost::bind 可以將其“第一個”參數綁定為 s1 對象。

[5] command 模式,其中封裝的 command 對象就是一個延遲調用的函數對象,它暫時保存某函數及其調用的各個參數,并在恰當的時候調用該函數。

[6] boost::ref(s1) 生成一個 boost::reference_wrapper<S1>(s1) 對象,其語義與 裸引用 幾乎一樣,只不過具有拷貝構造,以及賦值語義,這有點像 java 里面的對象引用。具體介紹見 boost 的官方文檔。

[7] signal 成員指向連接到的 signal , signal_data 成員指向該函數在 signal 中保存的位置(一般為迭代器),而 signal_disconnect 則是個函數指針,負責斷開連接,將前兩個成員作為參數傳給它就可以斷開連接。

boost源碼剖析之:多重回調機制signal(下)


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦?。。?/p>

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: a4yy午夜| 国产网站在线播放 | 日本亚洲天堂网 | jizz18毛片| 偷拍自拍在线播放 | 天天更新天天久久久更新影院 | 亚洲综合色网 | 日韩精品在线播放 | 日本三级韩国三级香港三级a级 | 久久视频这里只精品99 | 久久精品蜜芽亚洲国产a | 妖精视频永久在线入口 | 男人的天堂在线视频 | 日韩精品视频美在线精品视频 | 精精国产xxxx视频在线 | 成人精品网 | 日韩专区中文字幕 | av电影免费播放 | 免费成人在线网站 | 天堂一区二区三区四区 | 精品中文字幕一区 | 国产精品成人在线观看 | 午夜色视频在线观看 | 国内精品视频免费观看 | 日韩avav | 一区二区播放 | 欧美xxxx狂喷水喷水 | 污污成人一区二区三区四区 | 欧美在线一区视频 | 俄罗斯厕所偷窥视频 | 日本免费三级网站 | 一级片免费在线观看 | 国产成人免费网站 | 欧美乱xxxxx强 | 日韩精品一区二区三区国语自制 | 狠狠的撸2015最新版狠狠的撸2015最新版 | 小视频在线观看免费 | 国产午夜精品一区二区三区嫩草 | 成人欧美| 免费一级做a爰片性色毛片 狠狠色欧美亚洲狠狠色www | 锵锵锵锵锵锵锵锵锵好大好湿软件 |