# Item 41: 理解 implicit interfaces(隱式接口)和 compile-time polymorphism(編譯期多態)
作者:Scott Meyers
譯者:fatalerror99 (iTePub's Nirvana)
發布:http://blog.csdn.net/fatalerror99/
object-oriented programming(面向對象編程)的世界是圍繞著 explicit interfaces(顯式接口)和 runtime polymorphism(執行期多態)為中心的。例如,給出下面這個(沒有什么意義的)class(類),
```
class Widget {
public:
Widget();
virtual ~Widget();
virtual std::size_t size() const;
virtual void normalize();
void swap(Widget& other); // see Item 25
...
};
```
以及這個(同樣沒有什么意義的)function(函數),
```
void doProcessing(Widget& w)
{
if (w.size() > 10 && w != someNastyWidget) {
Widget temp(w);
temp.normalize();
temp.swap(w);
}
}
```
我們可以這樣談論 doProcessing 中的 w:
* 因為 w 被聲明為 Widget 類型的引用,w 必須支持 Widget interface(接口)。我們可以在源代碼中找到這個 interface(接口)(例如,Widget 的 .h 文件)以看清楚它是什么樣子的,所以我們稱其為一個 explicit interface(顯式接口)——它在源代碼中顯式可見。
* 因為 Widget 的一些 member functions(成員函數)是虛擬的,w 對這些函數的調用就表現為 runtime polymorphism(執行期多態):被調用的特定函數在執行期基于 w 的 dynamic type(動態類型)來確定(參見 Item 37)。
templates(模板)和 generic programming(泛型編程)的世界是根本不同的。在那個世界,explicit interfaces(顯式接口)和 runtime polymorphism(執行期多態)繼續存在,但是它們不那么重要了。作為替代,把 implicit interfaces(隱式接口)和 compile-time polymorphism(編譯期多態)推到了前面。為了了解這是怎樣一種情況,看一下當我們把 doProcessing 從一個 function(函數)轉為一個 function template(函數模板)時會發生什么:
```
template<typename T>
void doProcessing(T& w)
{
if (w.size() > 10 && w != someNastyWidget) {
T temp(w);
temp.normalize();
temp.swap(w);
}
}
```
現在我們可以如何談論 doProcessing 中的 w 呢?
* w 必須支持的 interface(接口)是通過 template(模板)中在 w 身上所執行的操作確定的。在本例中,它顯現為 w 的 type (T) 必須支持 size,normalize 和 swap member functions(成員函數);copy construction(拷貝構造函數)(用于創建 temp);以及對不等于的比較(用于和 someNastyWidget 之間的比較)。我們將在以后看到這并不很精確,但是對于現在來說它已經足夠正確了。重要的是這一系列必須有效地適合于模板編譯的表達式是 T 必須支持的 implicit interface(隱式接口)。
* 對諸如 operator> 和 operator!= 這樣的包含 w 的函數的調用可能伴隨 instantiating templates(實例化模板)以使這些調用成功。這樣的 instantiation(實例化)發生在編譯期間。因為用不同的 template parameters(模板參數)實例化 function templates(函數模板)導致不同的函數被調用,因此以 compile-time polymorphism(編譯期多態)著稱。
即使你從沒有使用過模板,你也應該熟悉 runtime(運行期)和 compile-time polymorphism(編譯期多態)之間的區別,因為它類似于確定一系列重載函數中哪一個應該被調用的過程(這個發生在編譯期)和 virtual function(虛擬函數)調用的 dynamic binding(動態綁定)(這個發生在運行期)之間的區別。explicit(顯式)和 implicit interfaces(隱式接口)之間的區別是與 template(模板)有關的新內容,需要對他進行近距離的考察。
一個 explicit interface(顯式接口)由 function signatures(函數識別特征)組成,也就是說,函數名,參數類型,返回值,等等。例如,Widget class(類)的 public interface(顯式接口),
```
class Widget {
public:
Widget();
virtual ~Widget();
virtual std::size_t size() const;
virtual void normalize();
void swap(Widget& other);
};
```
由一個 constructor(構造函數),一個 destructor(析構函數),以及函數 size,normalize 和 swap 組成,再加上 parameter types(參數類型),return types(返回類型)和這些函數的 constnesses(常量性)。(它也包括 compiler-generated(編譯器生成)的 copy constructor(拷貝構造函數)和 copy assignment operator(拷貝賦值運算符)——參見 Item 5。)它還可能包含 typedefs,還有,如果你膽大包天敢于違背 Item 22 的讓 data members(數據成員)private(私有)的建議,即使在這種情況下,這些 data members(數據成員)也不是。
一個 implicit interface(隱式接口)有很大不同。它不是基于 function signatures(函數識別特征)的。它是由 valid expressions(合法表達式)組成的。再看一下在 doProcessing template 開始處的條件:
```
template<typename T>
void doProcessing(T& w)
{
if (w.size() > 10 && w != someNastyWidget) {
...
```
對于 T(w 的類型)的 implicit interface(隱式接口)看起來有如下這些約束:
* 它必須提供一個名為 size 的返回一個正數值的 member function(成員函數)。
* 它必須支持一個用于比較兩個類型 T 的對象的 operator!= 函數。(這里,我們假定 someNastyWidget 的類型為 T。)
由于 operator overloading(運算符重載)的可能性,這兩個約束都不必滿足。是的,T 必須支持一個 size member function(成員函數),值得提及的是雖然這個函數可以是從一個 base class(基類)繼承來的。但是這個 member function(成員函數)不需要返回一個整數類型。它甚至不需要返回一個數值類型。對于這種情況,它甚至不需要返回一個定義了 operator> 的類型!它要做的全部就是返回類型 X 的一個 object(對象),有一個 operator> 可以用一個類型為 X 的 object(對象)和一個 int(因為 10 為 int 類型)來調用。這個 operator> 不需要取得一個類型 X 的參數,因為它可以取得一個類型 Y 的參數,只要在類型 X 的 objects(對象)和類型 Y 的 objects(對象)之間有一個 implicit conversion(隱式轉型)就可以了!
類似地,T 支持 operator!= 也是沒有必要的,因為如果 operator!= 取得一個類型 X 的 objects(對象)和一個類型 Y 的 objects(對象)是可接受的一樣。只要 T 能轉型為 X,而 someNastyWidget 的類型能夠轉型為 Y,對 operator!= 的調用就是合法的。
(一個旁注:此處的分析沒有考慮 operator&& 被重載的可能性,這會將上面的表達式的含義從與轉換到某些大概完全不同的東西。)
第一次考慮 implicit interfaces(隱式接口)的時候,大多數人都會頭疼,但是他們真的不需要阿司匹林。implicit interfaces(隱式接口)簡單地由一套 valid expressions(合法表達式)構成。這些表達式自身看起來可能很復雜,但是它們施加的約束通常是簡單易懂的。例如,給出這個條件,
```
if (w.size() > 10 && w != someNastyWidget) ...
```
關于 functions size,operator>,operator&& 或 operator!= 上的約束很難說出更多的東西,但是要識別出整個表達式的約束是非常簡單的。一個 if 語句的條件部分必須是一個 boolean expression(布爾表達式),所以不管 "w.size() > 10 && w != someNastyWidget" 所產生的類型涉及到的精確類型,它必須與 bool 兼容。這就是 template(模板)doProcessing 施加于它的 type parameter(類型參數)T 之上的 implicit interface(隱式接口)的一部分。被 doProcessing 需要的 interface(接口)的其余部分是 copy constructor(拷貝構造函數),normalize 和 swap 的調用對于類型 T 的 objects(對象)來說必須是合法的。
implicit interface(隱式接口)對 template(模板)的 parameters(參數)施加的影響正像 explicit interfaces(顯式接口)對一個 class(類)的 objects(對象)施加的影響,而且這兩者都在編譯期間被檢查。正像你不能用與它的 class(類)提供的 explicit interface(顯式接口)矛盾的方法使用 object(對象)(代碼無法編譯)一樣,除非一個 object(對象)支持 template(模板)所需要的 implicit interface(隱式接口),否則你就不能在一個 template(模板)中試圖使用這個 object(對象)(代碼還是無法編譯)。
Things to Remember
* classes(類)和 templates(模板)都支持 interfaces(接口)和 polymorphism(多態)。
* 對于 classes(類),interfaces(接口)是 explicit(顯式)的并以 function signatures(函數識別特征)為中心的。polymorphism(多態性)通過 virtual functions(虛擬函數)出現在運行期。
* 對于 template parameters(模板參數),interfaces(接口)是 implicit(隱式)的并基于 valid expressions(合法表達式)。polymorphism(多態性)通過 template instantiation(模板實例化)和 function overloading resolution(函數重載解析)出現在編譯期。
- Preface(前言)
- Introduction(導言)
- Terminology(術語)
- Item 1: 將 C++ 視為 federation of languages(語言聯合體)
- Item 2: 用 consts, enums 和 inlines 取代 #defines
- Item 3: 只要可能就用 const
- Item 4: 確保 objects(對象)在使用前被初始化
- Item 5: 了解 C++ 為你偷偷地加上和調用了什么函數
- Item 6: 如果你不想使用 compiler-generated functions(編譯器生成函數),就明確拒絕
- Item 7: 在 polymorphic base classes(多態基類)中將 destructors(析構函數)聲明為 virtual(虛擬)
- Item 8: 防止因為 exceptions(異常)而離開 destructors(析構函數)
- Item 9: 絕不要在 construction(構造)或 destruction(析構)期間調用 virtual functions(虛擬函數)
- Item 10: 讓 assignment operators(賦值運算符)返回一個 reference to *this(引向 *this 的引用)
- Item 11: 在 operator= 中處理 assignment to self(自賦值)
- Item 12: 拷貝一個對象的所有組成部分
- Item 13: 使用對象管理資源
- Item 14: 謹慎考慮資源管理類的拷貝行為
- Item 15: 在資源管理類中準備訪問裸資源(raw resources)
- Item 16: 使用相同形式的 new 和 delete
- Item 17: 在一個獨立的語句中將 new 出來的對象存入智能指針
- Item 18: 使接口易于正確使用,而難以錯誤使用
- Item 19: 視類設計為類型設計
- Item 20: 用 pass-by-reference-to-const(傳引用給 const)取代 pass-by-value(傳值)
- Item 21: 當你必須返回一個對象時不要試圖返回一個引用
- Item 22: 將數據成員聲明為 private
- Item 23: 用非成員非友元函數取代成員函數
- Item 24: 當類型轉換應該用于所有參數時,聲明為非成員函數
- Item 25: 考慮支持不拋異常的 swap
- Item 26: 只要有可能就推遲變量定義
- Item 27: 將強制轉型減到最少
- Item 28: 避免返回對象內部構件的“句柄”
- Item 29: 爭取異常安全(exception-safe)的代碼
- Item 30: 理解 inline 化的介入和排除
- Item 31: 最小化文件之間的編譯依賴
- Item 32: 確保 public inheritance 模擬 "is-a"
- Item 33: 避免覆蓋(hiding)“通過繼承得到的名字”
- Item 34: 區分 inheritance of interface(接口繼承)和 inheritance of implementation(實現繼承)
- Item 35: 考慮可選的 virtual functions(虛擬函數)的替代方法
- Item 36: 絕不要重定義一個 inherited non-virtual function(通過繼承得到的非虛擬函數)
- Item 37: 絕不要重定義一個函數的 inherited default parameter value(通過繼承得到的缺省參數值)
- Item 38: 通過 composition(復合)模擬 "has-a"(有一個)或 "is-implemented-in-terms-of"(是根據……實現的)
- Item 39: 謹慎使用 private inheritance(私有繼承)
- Item 40: 謹慎使用 multiple inheritance(多繼承)
- Item 41: 理解 implicit interfaces(隱式接口)和 compile-time polymorphism(編譯期多態)
- Item 42: 理解 typename 的兩個含義
- Item 43: 了解如何訪問 templatized base classes(模板化基類)中的名字
- Item 44: 從 templates(模板)中分離出 parameter-independent(參數無關)的代碼
- Item 45: 用 member function templates(成員函數模板) 接受 "all compatible types"(“所有兼容類型”)
- Item 46: 需要 type conversions(類型轉換)時在 templates(模板)內定義 non-member functions(非成員函數)
- Item 47: 為類型信息使用 traits classes(特征類)
- Item 48: 感受 template metaprogramming(模板元編程)
- Item 49: 了解 new-handler 的行為
- Item 50: 領會何時替換 new 和 delete 才有意義
- Item 51: 編寫 new 和 delete 時要遵守慣例
- Item 52: 如果編寫了 placement new,就要編寫 placement delete
- 附錄 A. 超越 Effective C++
- 附錄 B. 第二和第三版之間的 Item 映射