正文

回到頂部

1. 顯示接口和運行時多態(tài)

面向?qū)ο缶幊痰氖澜鐕@著顯式接口和運行時多態(tài)。舉個例子,考慮下面的類(無意義的類),

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

 1 class Widget { 2 public: 3 Widget(); 4 virtual ~Widget(); 5  6 virtual std::size_t size() const; 7 virtual void normalize(); 8  9 void swap(Widget& other);                          // see Item 2510 11 ...                                                                 
12 13 };

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

 

考慮下面的函數(shù)(同樣沒有意義),

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

 1 void doProcessing(Widget& w) 2  3 { 4  5 if (w.size() > 10 && w != someNastyWidget) { 6  7 Widget temp(w); 8  9 temp.normalize();10 11 temp.swap(w);12 13 }14 15 }

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

 

對于doProcessing中的w,我們可以這樣說:

  • 因為w被聲明為Widget類型,w必須支持Widget接口。我們可以在源碼中搜尋這個接口(例如,在Widget的頭文件中),以便能夠確切的知道它長成什么樣子,所以我將其叫做一個顯式的接口(explicit interface)——可以顯式的在源碼中看到的接口。

  • 因為Widget中的一些成員函數(shù)是虛的,w對這些函數(shù)的調(diào)用會展示出運行時多態(tài):w具體調(diào)用哪個函數(shù)會根據(jù)運行時w的動態(tài)類型來決定。

 

回到頂部

2. 隱式接口和編譯期多態(tài)

模板(template)和泛型編程(generic programming)的世界從根本上發(fā)生了變化。在這個世界中,顯式接口和運行時多態(tài)繼續(xù)存在,但是它們不再像以前那么重要。相反,隱式接口和編譯時多態(tài)被挪到了前臺。為了了解這是什么樣子的,我們將doProcessing從函數(shù)轉(zhuǎn)換為一個函數(shù)模板,看看會發(fā)生什么:

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

 1 template<typename T> 2  3 void doProcessing(T& w) 4  5 { 6  7 if (w.size() > 10 && w != someNastyWidget) { 8  9 T temp(w);10 11 temp.normalize();12 13 temp.swap(w);14 15 }16 17 }

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

 

現(xiàn)在我們能對doProcessing中的w說些什么呢?

  • W必須支持的接口由模板中w需要執(zhí)行的操作所決定。例如,w的類型T必須支持size,normalize和swap成員函數(shù);拷貝構(gòu)造函數(shù)(來創(chuàng)建temp);和不等比較(同someNastyWidget進行比較)。我們很快就能發(fā)現(xiàn)這也不是很精確的,但是對于現(xiàn)在來說足夠了。重要的是,這些表達(dá)式必須是T所支持的隱式接口,它們對于模板來說必須是有效的以便能夠通過編譯。

  • 對于涉及到w的像operator>和operator!=這樣的函數(shù)調(diào)用,可能涉及到模板的實例化來讓這些調(diào)用成功。這些實例化在編譯期發(fā)生。因為用不同的模板參數(shù)實例化出來的函數(shù)模板會導(dǎo)致不同的函數(shù)被調(diào)用,這叫做“編譯時多態(tài)”。

 

回到頂部

3. 顯示接口和隱式接口的區(qū)別

 

3.1 顯示接口的特點

即使你永遠(yuǎn)不使用模板,你也應(yīng)該熟悉運行時多態(tài)和編譯期多態(tài)的區(qū)別,因為這同編譯期決定調(diào)用哪個重載函數(shù)以及運行期決定綁定哪個虛函數(shù)是類似的。隱式和顯式接口的區(qū)別對于模板來說是新的概念,然而,一個顯式的接口由函數(shù)簽名組成,也即是函數(shù)名字,參數(shù)類型,返回值類型等等。Widget類的公共接口,例如:

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

1 class Widget {2 public:3 Widget();4 virtual ~Widget();5 virtual std::size_t size() const;6 virtual void normalize();7 void swap(Widget& other);8 };

大數(shù)據(jù)培訓(xùn),云培訓(xùn),數(shù)據(jù)挖掘培訓(xùn),云計算培訓(xùn),高端軟件開發(fā)培訓(xùn),項目經(jīng)理培訓(xùn)

 

由一個構(gòu)造函數(shù),一個析構(gòu)函數(shù),和函數(shù)size,normalize和swap以及參數(shù)類型,返回值類型和這些函數(shù)的常量性組成。(同樣包含編譯器生成的拷貝構(gòu)造函數(shù)和拷貝賦值運算符——看Item 5)。它同樣可以包含typedef和數(shù)據(jù)成員,如果你夠大膽違反Item22的建議的話(將數(shù)據(jù)成員聲明為private)。雖然在這個例子中沒有這么做。

3.2 隱式接口的特點

一個隱式的接口會有很大的不同。它不是基于函數(shù)簽名。而是由有效表達(dá)式組成。再看一下doProcessing模板開始部分的條件表達(dá)式:

1 template<typename T>2 void doProcessing(T& w)3 {4 if (w.size() > 10 && w != someNastyWidget) {5 ...

 

T(w的類型)的隱式接口看上去會有如下限制:

  • 它必須提供一個名字為size的成員函數(shù)并且返回一個整型值。

  • 它必須支持!=操作符函數(shù),能夠?qū)蓚€T類型的對象進行比較。(這里,我們假設(shè)someNastWidget的類型為T。)

多虧了操作符重載,上面的兩個限制都不需要滿足。T必須支持一個size成員函數(shù),值得提及的是這個函數(shù)可能繼承自一個基類。但是這個成員函數(shù)沒有必要返回一個整型值。甚至不需要返回一個數(shù)字類型值。如果這么說的話,它甚至不需要返回operator>定義中所需要的值。他需要的是返回一個類型X的對象,于是可以在一個類型X對象和int(因為10是int型的)型對象上調(diào)用operator>。但是Operator>沒有必要帶一個類型X的參數(shù),因為它也可以帶一個類型Y的參數(shù),只要Y可以隱式的轉(zhuǎn)成X就可以了。

 

類似的,T也沒有必要支持operator!=,因為operator!=帶一個類型X的參數(shù)和一個類型Y的參數(shù)也能接受。只要T能轉(zhuǎn)成X并且someNastyWidget的類型可以轉(zhuǎn)換成Y,那么函數(shù)調(diào)用就是有效的。

 

(說句題外話,這個分析沒有考慮將operator&&進行重載的可能性,這樣就將上面的表達(dá)式的意思從一個連接詞轉(zhuǎn)換成了其它的意義迥然的東西。)

 

大多數(shù)人當(dāng)?shù)谝淮伍_始考慮這種隱式轉(zhuǎn)換就頭疼,你不需要吃阿司匹林。隱式接口只是簡單的由一些有效表達(dá)式組成。表達(dá)式本身看起來復(fù)雜,但是加在上面的限制一般來說是簡單直接的。例如,考慮下面的條件表達(dá)式,

1 if (w.size() > 10 && w != someNastyWidget) ...

很難說要對函數(shù)size,operator>,operator&&或者operator!=做什么限制,但是很容易辨認(rèn)出需要對整個表達(dá)式做出的限制。If聲明的條件部分必須是一個boolean表達(dá)式,所以不管涉及到什么類型,也不管w.size() > 10 && w != someNastyWidget產(chǎn)生什么,它必須同bool是兼容的。這是模板doProcessing強加在類型參數(shù)T上的隱式接口的一部分。剩下的doProcessing所需要的接口就是對拷貝構(gòu)造函數(shù)的調(diào)用,還有swap對于類型T來說必須是有效的。

 

強加在模板參數(shù)上的隱式接口同強加在類對象上的顯示接口一樣真實,兩者都是在編譯階段檢查。你不能同一個類提供的顯示接口相矛盾的方式使用一個類對象(不會編譯通過),你也不能隨便在一個模板中嘗試使用一個對象,除非這個對象支持模板需要的隱式轉(zhuǎn)換(否則也不能通過編譯)

回到頂部

4. 總結(jié)

  • 類和模板都支持接口和多態(tài)。

  • 對于類來說,接口是顯示的,以函數(shù)簽名為中心。多態(tài)發(fā)生在運行時,通過虛函數(shù)來實現(xiàn)。

  • 對于模板參數(shù)來說,接口是隱式的,基于有效表達(dá)式。模板多態(tài)通過模板實例化和函數(shù)重載來實現(xiàn),它發(fā)生在編譯期。


作者: HarlanC 

博客地址: http://www.cnblogs.com/harlanc/ 
個人博客: http://www.harlancn.me/ 
本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出, 原文鏈接 . 

http://www.cnblogs.com/harlanc/p/6649086.html