題外話:侯捷老師可貴一年就來上九堂課就要會寶島,特此留念簽名贈語及合照以自勉。java
學海無涯,爲勤是岸c++
<正文開始>編程
昨天晚上連上了3個小時的大課探究單單講了Adapter一個類,幸運的是本人剛好在大一的時候接觸過好比<functioinal>庫類中的bind1st,bind2nd這些函數對象的使用方法,畢竟若要使用<algorithm>的話,裏面幾乎每個函數都須要咱們把模版中的函數對象好比Comparato之類的重寫一下,可是真的沒有想到,這些庫這些類使用起來明明那麼簡單可是在老師的講解下里面的結構和設計人員的思路原來真的是複雜縝密。安全
具體說Adapter(適配器)這種設計以前必需要先簡單說說LSP(里氏替換原則),其內容是:子類(derived typed)必須可以替換掉它的基類(base type)。這一句話就有點讓我摸不着頭腦了,既然是子類已經從基類繼承過來了,那豈不就是子類必然可以替換掉父類麼?否則多態(polyporphism)還從和談起?若是是在java裏的話,(我以爲)也許這點是毫無爭議的由於java有強大的繼承體系一切類都要從Object類繼承而來(lambda表達式的目標對象也不是一種類,因此不算從Object繼承而來),然而在C++語言之中另有一種獨特的設計--private繼承,這種獨特的繼承方式會是父類中不管是protected仍是public繼承抑或是多重繼承過來的指針引用都轉變爲子類的private數據,也就是說這些數據在繼承樹的這條分支上已經被宣告了終結。因此象在MFC這樣的庫的創建的時候LSP原則是必須遵照的,否則會給程序帶來(嚴重的)隱患。架構
課件中所示,Adapter設計通常有三種(昨天只講了兩種):less
(1)Container Adapter函數
這個庫的底層容器咱們能夠看到選擇的是deque,值得注意的是在這套設計之中queue和deque是複合的關係,可是和我猜想的相反,queue包含了deque類也就是說queue是在deque之上架構起來的,至於爲何明明deque和(雙驅)list有相同的「效果」甚至有相同的方法命名卻選擇了deque來做爲默認的底層容器,老師謙虛地講他也不敢下定論,可是咱們至少能夠大膽地推測deque的速度或者效率是優於list的。並且在這裏面queue「HAS-A」deque這樣的關係,在這個結構之中架構起來的更多的容器基本上都是以底層容器出發,改寫方法添加約束從而完成架構。在《More Effective C++》中的編程準則之中有提到過,建議編程者將一切的數據寫入private之中,可是這並不意味着這個類在繼承鏈上部分數據的殘缺,由於咱們會在public或者protected中聲明它們的getter&setter(是否想起了java呢:-P)來不斷地繼承過來對這個數據的操做權從而實現安全架構。spa
##stack容器的實現和queue的實現是極其類似的,這裏很少贅述##設計
(2)Function Adapter指針
這部分較上部分較難懂的,例子中的Adapter簡單的來說就是要實現一個功能:參數綁定。有的函數對象做爲參數可能只須要一個參數來進行調用,可是咱們這裏卻只用一個雙參的函數對象沒法直接使用做爲參數,那麼最效率的解決方法就是使用Adapter。固然不要自信地認爲會用了這個binder系列not系列函數就是理解了Adapter,深刻了解架構,才能幫助咱們寫出本身想要的Adapter,瞭解STL的規範才能夠幫助咱們寫出的Adapter更好地與上層容器/上層對象相兼容。好比要寫出一個三參綁爲一個四參綁爲兩個這些特殊的狀況纔會有良好的方案去解決。
例子是假設咱們要使用count_if函數,這個函數在<algorithm>庫中的做用是if xx then count計數,其中的第一個參數和第二個參數固然是Iterator類型,然而第三個參數咱們須要一個單參的Predicate函數對象或者函數指針。以下圖所示,其實在這裏面Predicate只是template中的一個命名,它能夠是T,Fxxx各類名字,可是一個Predicate的名字就可讓其餘人看出這個函數對象最後須要返回的是一個布爾值,若是一旦模版實例化爲一個將操做符重載爲例如void operator()(Paramlist..)這樣的函數時,那麼在count_if內部的判斷分支語言就會遇到麻煩,並且因爲c++的靈活性若是將返回值重載爲int /long類的話,這樣count_if就不會報錯程序照常執行的同時,會給貌似正確程序埋下隱患。
那麼Adapter是怎麼樣架構起來的呢?下面這張圖很好地歸納了下來。以下圖所示(其實那個f和x之間按理來講應該有個逗號Anyway~)Adapter的實現過程就是在構造的時候將須要綁定的參數綁定下來,而後重載操做符使之成爲一個新的函數對象。是的,貌似很是簡單,可是接着向下看,若是考慮到functionAdapter是從unary_function和binary_function繼承而來的模版結構的話,會大大增長複雜度,整個搭建起來adapter的過程也是不斷地向父類「詢問」typedef類型的過程,這些約束是adapter中最難理解的部分。
最後咱們再來看一下具體的實現宏觀過程:
##課後提問##
我:在C++11的新特性中已經容許了自動類型推斷,而爲何黑板上強調的bind2nd的模版類型必須和參數類型代表一致?:::(黑板)bind2nd<less<int>>(less<int>(),12)
侯捷:由於C++11的自動類型推斷只能運用在模版函數之中,可是對於模板函數對象,咱們必須先後代表參數和函數對象的模版。