上面的 Person class(類)示范了 has-a(有一個)的關系。一個 Person object(對象)has a(有一個)名字,一個地址,以及語音和傳真電話號碼。你不能說一個人 is a(是一個)名字或一個人 is an(是一個)地址。你可以說一個人 has a(有一個)名字和 has an(有一個)地址。大多數人對此區別不難理解,所以混淆 is-a和 has-a(有一個)之間的角色的情況非常少見。
is-a和 is-implemented-in-terms-of(是根據……實現的)之間的區別稍微有些棘手。例如,假設你需要一個類的模板來表現相當小的 objects(對象)的 sets,也就是說,排除重復的集合。因為 reuse(復用)是一件受人歡迎的事情,你的第一個直覺就是使用標準庫中的 set template(模板)。當你能使用已經被寫好的東西時,為什么還要寫一個新的 template(模板)呢?
reuse(復用)依然是一件受人歡迎的事情。作為 data strUCture(數據結構)的專家,你知道實現 sets 的諸多選擇,其中一種是使用 linked lists(線性鏈表)。你也知道標準的 C++ 庫中有一個 list template(模板),所以你決定(復)用它。
具體地說,你決定讓你的新的 Set template(模板)從 list 繼續。也就是說,Set<T> 將從 list<T> 繼續。究竟,在你的實現中,一個 Set object(對象)實際上就是一個 list object(對象)。于是,你就像這樣聲明你的 Set template(模板):
template<typename T> // the wrong way to use list for Set class Set: public std::list<T> { ... }; 在這里,看起來每件事情都很好。但實際上有一個很大的錯誤。就像《C++箴言:確保公開繼續模擬“is-a”》中的解釋,假如 D is-a(是一個)B,對于 B 成立的每一件事情對 D 也成立。然而,一個 list object(對象)可以包含重復,所以假如值 3051 被插入一個 list<int> 兩次,那個 list 將包含 3051 的兩個拷貝。與此對照,一個 Set 不可以包含重復,所以假如值 3051 被插入一個 Set<int> 兩次,那個 set 只包含該值的一個拷貝。因此一個 Set is-a(是一個)list 是不正確的,因為對 list objects(對象)成立的某些事情對 Set objects(對象)不成立。
因為這兩個 classes(類)之間的關系不是 is-a(是一個),public inheritance(公有繼續)不是模擬這個關系的正確方法。正確的方法是熟悉到一個 Set object(對象)可以 be implemented in terms of a list object(是根據一個 list 對象實現的):
template<class T> // the right way to use list for Set class Set { public: bool member(const T& item) const;
private: std::list<T> rep; // representation for Set data }; Set 的 member functions(成員函數)可以極大程度地依靠 list 和標準庫的其它部分已經提供的機能,所以只要你熟悉了用 STL 編程的基本方法,實現就非常簡單了: