言歸正傳,sigslot是乙個用標準c++語法實現的訊號與槽機制的函式庫,型別和執行緒安全。提到訊號與槽機制,恐怕最容易想到的就是大名鼎鼎的qt所支援的物件之間通訊的模式吧。不過這裡的訊號與槽雖然在概念上等價與qt所實現的訊號與槽,但是採用的僅僅是標準c++語法,不像qt採用了擴充套件c++語言的方式(qt需要使用moc工具對**預處理之後,才能由標準的c++編譯器進行編譯)。
眾所周知,c++是一門特性眾多的語言,其支援多種程式設計正規化。雖然c++在一定程度上支援oop程式設計,但是c++這種「靜態訊息機制」的語言一直沒有實現物件級別的delegate機制,而c++之父bjarne主張的「庫擴充套件勝於語言擴充套件」的做法使得各種解決方案層出不窮。除了訊號與槽機制,c++11正式加入的std:bind/std::function組合也提供了優秀的解決方案。這裡所說的訊號與槽機制也是一種物件間通訊的機制,具體的討論也可以看看sigslot相關介紹中的內容。
sigslot主頁:
sigslot文件: /sigslot.pdf
sigslot 庫的用法文件中已然很明了了,所以在這裡就不贅述了。接下來我們看看這個庫的實現。原始碼分析的方法有很多種,具體到庫**的分析的方法,我喜歡的是先研究庫的功能,直到能寫出乙個demo程式為止。研究乙個庫的前提是你得會用它,熟悉它的介面。讀完文件,很容易就寫出了下面的測試**:
#include
#include
"sigslot.h"
using
namespace sigslot;
class switch
;class light : public has_slots<>
};int
main
(int argc, char *argv)
使用方法很簡單。從這裡我們就能看出來,這個庫無非就是在訊號那一端儲存了這個訊號所繫結的函式指標,在槽函式這一端儲存了其繫結的訊號而已。接下來的問題實際上就是採用合理的資料結構來處理問題了。
sigslot庫簡單到只有乙個標頭檔案 sigslot.h ,開啟後洋洋灑灑幾千行**,其實仔細看看絕大多數**都是為了適應引數數量不同的成員函式指標的定義,為其擴充套件的模版**。從定義上看,這個庫支援 0~8 個引數的成員函式繫結。在紙上畫一下類的繼承關係,很容易就得到了如下的函式繼承圖( ide 有相關的工具也可以拿來用 ~ ) :
從這個圖上看,其實**關係已經很清晰了。 實現了槽函式的類需要繼承 has_slots 類。而 has_slots類 擁有乙個 std::set<_signal_base> 型別的容器(所有的 mt_policy 其實是庫定義的三種鎖策略而已[單執行緒無鎖、多執行緒共享全域性鎖、多執行緒區域性鎖])。所有的 _signal_base[0-8] 的類持有各自的 std::list<_connection_base> 的list容器,而 _connection_base[0-8] 則分別封裝了0~8個引數的成員函式的指標。
這裡的重複**是很多的,作為分析的話完全可以每中類**只留下乙個,這樣所有的**就精簡到只有6個類了(反正別的也只是為了適應引數個數寫的模版罷了,**除了引數個數外都是一樣的)。
至於前面說到的鎖,其實也只是因為c++ stl庫中的容器本身不是執行緒安全的,需要在外部加鎖。鎖的實現很平常,另外用c++ raii手法封裝的lock_block類也是常見的用法。唯一需要注意的是,這個庫在使用了訊號與槽的使用者類發生了拷貝構造時,其訊號與槽的繫結關係也會被拷貝,所以**中的類都自行編寫了相關的拷貝建構函式。這裡稍微解釋下,如果a類的a物件的x訊號和b類的b1物件的y函式繫結,然後用b1初始化構造b2(即 b b2(b1))。這時候,a類的a物件的x訊號也會繫結到b2物件的y函式。這個特性我感覺有點莫名其妙,而且使得**複雜了不少(我覺得沒必要這麼設計,使用者需要這個特性的話,自己再呼叫一次繫結函式就是了)。
知曉了基本的原理之後,看**就很容易了。比如在擁有訊號和擁有槽函式的物件析構時,會自動的取消掉之前的繫結,**很清晰易讀的。下面是我自己根據sigslot的原理,簡化出來的**,大家可以先看看然後去讀sigslot的原始碼會簡單很多。
boost 原始碼 ref 庫分析
引用檔案 boost ref.hpp 一般情況下,泛型演算法中的函式物件,傳值語義是可行的,但是也有很多特殊情況,作為引數的函式物件拷貝代價過高 具有複雜的內部狀態 或者不希望拷貝物件 內部狀態不應該被改變 甚至拷貝是不可行的 noncopyable,單件 boost.ref應用 模式,引入物件引用...
muduo base庫原始碼分析(七)
執行緒池問題本質上也是生產者 消費者問題,生產者向任務佇列中新增任務,執行緒佇列中的執行緒從任務佇列中取出任務執行。task函式是要執行的任務 threads 是執行緒佇列,是乙個ptr vector,內部存放執行緒指標 queue 是任務佇列 start函式用來啟動執行緒池 建立固定個數的執行緒 ...
spring原始碼分析 spring原始碼分析
1.spring 執行原理 spring 啟動時讀取應用程式提供的 bean 配置資訊,並在 spring 容器中生成乙份相應的 bean 配置登錄檔,然後根據這張登錄檔例項化 bean,裝配好 bean 之間的依賴關係,為上 層應用提供準備就緒的執行環境。二 spring 原始碼分析 1.1spr...