程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> C++箴言:理解隱式接口和編譯期多態

C++箴言:理解隱式接口和編譯期多態

編輯:關於C++

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(動態類型)來確定(參見《C++箴言:絕不重定義繼承的非虛擬函數》)。

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(拷貝賦值運算符)——參見《C++箴言:了解C++偷偷加上和調用了什麼》)它還可能包含 typedefs,還有,如果你膽大包天敢於違背讓 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(函數重載解析)出現在編譯期。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved