Item 41:隱式介面與編譯期多型
阿新 • • 發佈:2019-01-07
Item 41: Understand implicit interfaces and compile-time polymorphism.
面向物件設計中的類(class)考慮的是顯式介面(explicit interface)和執行時多型, 而模板程式設計中的模板(template)考慮的是隱式介面(implicit interface)和編譯期多型。
- 對類而言,顯式介面是由函式簽名表徵的,執行時多型由虛擬函式實現;
- 對模板而言,隱式介面是由表示式的合法性表徵的,編譯期多型由模板初始化和函式過載的解析實現。
顯式介面和執行時多型
一個類的顯式介面是由public成員函式簽名(包括函式名、引數型別、返回值型別等)、型別定義(typedef)、public資料成員構成的。
class Widget{
public:
Widget();
virtual ~Widget();
virtual size_t size() const;
virtual void normalize();
void swap(Widget& other);
};
void doProcessing(Widget& w){
if(w.size() > 10 && w != someOne){
Widget tmp(w);
tmp.normalize();
tmp.swap(w );
}
}
對於doProcesing中的w,我們可以知道:
- w應支援Widget的介面,包括:normalize(), swap()等。這些介面在原始碼中是可以找到的,稱為顯式介面。
- Widget有些成員函式是virtual的,會表現出執行時多型:具體的被呼叫者會根據Widget的動態型別而決定。
隱式介面和編譯期多型
在模板和類屬程式設計(generic programming)中這一點完全不同,在這裡隱式介面和編譯期多型更為重要:
template<typename T>
void doProcessing(T& w){
if(w.size() > 10 && w!= someOne){
T tmp(w);
tmp.normalize();
tmp.swap(w);
}
}
現在的doProcessing是一個函式模板,其中的w有所不同:
- w應支援的介面取決於模板中w上的操作。比如:w(型別T)必須支援size, normalize, swap方法;拷貝建構函式;不等運算子。 總之,這些表示式必須合法而且通過編譯構成了w應支援的介面。
- 其中的operator>和operator!=要呼叫成功可能需要例項化一些模板,而使用不同的模板引數例項化模板的過程就是編譯期多型。
具體來講,T的隱式介面應滿足:
- 必須包含一個返回值為整型的成員函式;
- 支援一個接受T型別的operator!=函式。
但由於C++的運算子過載和隱式型別轉換特性,上述兩個條件都不需要滿足。 首先size可能是繼承來的函式而非T的成員函式,但它不需要返回一個int,甚至不需要返回一個數字型別,返回型別也不需要定義operator>。 它需要返回的型別X只需滿足:operator>可以接受X和int。但operator>的第一個引數型別可以不是X,只要X能隱式轉換為它的第一個引數型別即可。 類似地,operator!=介面也有極大的靈活性。
當你想到這些約束時可能真的會頭大,但實踐中比這些直觀的多,介面只是由合法的表示式構成的。 例如下面的表示式看起來就很直觀:
if (w.size() > 10 && w != someOne) ...
總之隱式介面和顯式介面一樣地真實存在,在編譯時都會進行檢查。正如你錯誤地使用顯式介面會導致編譯錯一樣, 物件不支援模板所要求的隱式介面也會導致編譯錯。