05.14 C++模版從精通到精神分裂

這是一篇寫軟件的文章,但是很硬,提前預警一下,女生不要看!

所有寫C++的文章,如果沒有源代碼都是在耍流氓。閒話不說, May the source be with you!

C++模版從精通到精神分裂

這是一個教科書般經典的例子。介紹C++的繼承和多態。 這裡唯一需要重點強調的是:對函數LetAnimalTalk和vector va 來說,我們可以想象他們是客戶。[face=黑體]通過繼承把變化封裝到基類的後面,這樣使用基類接口的客戶就不需要改動![/face]對客戶來說,無論基類後面怎麼變化,你都影響不到我。例如,如果現在有一個經理狗加入了項目團隊,你的LetAnimalTalk函數是不需要任何改變的。

So far so good! 現在看看引入模版後,發生了什麼?

C++模版從精通到精神分裂

基本的應用場景是這樣的。對於animal, 你可以用字符串來表示他的ID, 如果你想developer是不應該享有字符串名字的,那麼你也可以用整型數來表示他的ID。上面整個的程序,如果你把main中換成下面的樣子,除了貓會有點意見,其它一切都沒有問題!

C++模版從精通到精神分裂

上面模版繼承的一個最明顯的弊端是語法變得更加臃腫和複雜了。例如,你不可能在子類中直接引用基類的變量了。如果你引用他,必須使用Animal::id_這樣的語法。背後的原因是當編譯器編譯Cat的時候,Animal是根本不存在的!具體的細節你可以看後面的參考文獻1。

另外的一個問題就是虛函數的效率問題。這種多態是發生在程序運行的時候,主要通過虛函數表進行調用分發。所以有一定的效率損失。下面我們再看另外一個例子。 由於貓不喜歡整型ID的名字,所以我們這裡完全去掉這個feature,重點關注如何利用模版實現多態。

C++模版從精通到精神分裂

這段代碼中,有幾點注意一下:

1) 多態已經不需要用指針了,我們可以用引用來支持多態。

2)在函數LetAnimalTalk中,pa.talk()到底調用那一個 talkImplement是在編譯的時候就決定了。這是一種靜態多態技術。所以沒有效率的損失。

3)但是函數LetAnimalTalk現在必須是模版函數,同時我們也失去了用vector同時保存Cat 和Developer的能力。這是效率提升帶來的靈活性的損失!

4)這個是CRTP模式,更多介紹看參考文獻2。翻譯過來就是“好奇地不斷追問自己!”這應該是一種精神分裂的明顯的初期症狀了。

目前為止,我們介紹了三個例子。還都遵循著一個基本的IS-A的邏輯關係。也就是說,Cat是一個Animal,Developer也是一個Animal。下面介紹三個IMPLEMENT-BY的邏輯關係。第一個例子完全沒有使用繼承。

C++模版從精通到精神分裂

1) 這段程序中,通過模版參數,在編譯的時候就把不同的talk行為的實現方式傳遞給Animal類。這個方法在STL中運用的相當廣泛。具體的例子像STL中的map類

C++模版從精通到精神分裂

其中, std::less就類似於我們上面的SayMiao。

2)由於導入的是某種行為,所有再叫做Cat就不合適了,所以這裡把類的名字叫做SayMiao

為了實現IMPLEMENT-BY關係,我們也可以使用私有繼承:

C++模版從精通到精神分裂

1) 有沒有被

template

class Animal: private T{

這樣的語法驚到!沒關係,我們慢慢來。首先私有繼承不是IS-A的關係。而是IMPLEMENT-BY的關係。關於什麼時候使用私有繼承,什麼時候使用組合(composition)。請看參考文獻3。

2)這種方式是Parameterised inheritance, 也是一種常見的設計模式,請看參考文獻4

OK,最後的問題是,既然私有繼承可以,共有繼承行不行?在一個分裂的病人眼中,沒啥是不行的!

C++模版從精通到精神分裂

1) 這就是在Modern C++ design中提到的Policy-Based design。一個小提示是:現在在Animal中已經不需要talk這個函數了。

上面我一共給出了六個程序。到 https://www.onlinegdb.com/ 把這六段代碼拷貝進去,根據自己的理解和問題修改一下。“紙上得來終覺淺,絕知此事要運行”。 這其實是陸游給廣大程序猿的一句忠告。 如果你有足夠的耐心,你可以慢慢地深入的體會,這裡好玩的東西還挺多的。由於篇章關係(主要是再展開我也不會了!)我就不多說了。

以上這六段程序,分別代表著六種不同的語法方式,表達出兩種最基本的設計模式 IS-A還是IMPLEMENT-BY。首先,沒有什麼優劣之分,在不同的場景下,各有優缺點。另外,C++的模版完全不同於傳統的C++編程。他的語法和想表達的語義有非常明顯的分裂趨勢,非常容易把傳統的C++程序猿也搞分裂了。正所謂範型是C++最大的坑,但是不跳此坑,不足以談人生!

如果沒有看懂就算了!你完全可以說:“這個人已經瘋了!” 這個我在標題中已經承認了!

參考文獻

1)https://eli.thegreenplace.net/2012/02/06/dependent-name-lookup-for-c-templates

2)https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

3)https://isocpp.org/wiki/faq/private-inheritance#priv-inherit-vs-compos

4)https://blog.feabhas.com/2014/06/template-inheritance/



分享到:


相關文章: