《Effective C++ 中文版 第三版》讀書筆記
條款 38:通過復(fù)合塑模出 has-a 或 “根據(jù)某物實(shí)現(xiàn)出”
復(fù)合(composition)是類型之間的一種關(guān)系揭厚,當(dāng)某種類型的對象內(nèi)含它種類型的對象没龙,便是這種關(guān)系:
class Address {...};
class PhoneNumber {...};
class Person {
public:
...
private:
std::string name; // 合成成分物
Address address; // 合成成分物
PhoneNumber voiceNumber; // 合成成分物
PhoneNumber faxNumber; // 合成成分物
};
public 繼承帶有 is-a 的意義编丘。復(fù)合也有它自己的意義确虱。實(shí)際上它有兩個意義抢韭。復(fù)合意味 has-a 或 is-implemented-in-terms-of凳谦。那是因?yàn)槟阏蛩阍谀愕能浖刑幚韮蓚€不同的領(lǐng)域念恍。程序中的對象其實(shí)相當(dāng)于你所塑造的世界中的某些事物驹尼,例如人趣避、汽車、等等新翎。這樣的對象屬于應(yīng)用域(application domain)部分程帕。其他對象則純粹是實(shí)現(xiàn)細(xì)節(jié)上的人工制品住练,像是緩沖區(qū)、互斥器愁拭、查找樹等等讲逛。這些對象相當(dāng)于你的軟件的實(shí)現(xiàn)域(implementation domain)。當(dāng)復(fù)合發(fā)生在應(yīng)用域內(nèi)的對象之間岭埠,表現(xiàn)出 has-a 的關(guān)系盏混;當(dāng)復(fù)合發(fā)生在實(shí)現(xiàn)域內(nèi)則是表現(xiàn) is-implemented-in-term-of 的關(guān)系。
上述的 Person class 示范的是 has-a 關(guān)系惜论。
比較麻煩的是區(qū)分 is-a 和 is-implemented-in-term-of 這兩種對象關(guān)系许赃。
假如你想制造出一組 class 用來表現(xiàn)有不重復(fù)對象組成的 sets。而 stl 中 sets 以平衡查找樹(balanced search trees)實(shí)現(xiàn)而成馆类,每個元素耗用 3 個額外指針混聊。而你的程序空間比速度重要。所以你決定復(fù)用 list template 采用 linked lists 來實(shí)現(xiàn)自己的 set乾巧。
你想讓 set 繼承 stl::list:
template<typename T>
class Set : public std::list<T>{...}; // 將 list 應(yīng)用于 set句喜。錯誤做法。
public 繼承是 is-a 關(guān)系卧抗,但 set 不是一種 list藤滥,因?yàn)閷?list 為真的某些事情對 set 對象并不為真。例如社裆,list 可以內(nèi)含重復(fù)元素,如果 30 被安插到 list<int> 兩次向图,那個 list 將內(nèi)含兩筆 30泳秀,如果 30 被安插到 set<int> 兩次,set 只內(nèi)含一筆 30榄攀。
所以這兩個 classes 之間并非 is-a 關(guān)系嗜傅。不應(yīng)該是 public 繼承,正確的做法是檩赢,set對象可根據(jù)一個 list 對象實(shí)現(xiàn)出來:
template<typename T>
class Set {
public:
bool member(const T& item) const;
void insert(const T& item);
void remove(const T& item);
std::size_t size() const;
private:
std::list<T> rep; // 用來表述 set 的數(shù)據(jù)
};
set 的成員函數(shù)可以大量依賴 list 及標(biāo)準(zhǔn)程序庫其他部分來完成吕嘀,所以其實(shí)現(xiàn)很直觀也很簡單,只要你熟悉 stl 編寫程序:
template<typename T>
bool Set<T>::member(const T& item) const
{
return std::find(rep.begin(), rep.end(), item) != rep.end();
}
template<typename T>
void Set<T>::insert(const T& item)
{
if(!member(item))
rep.push_back(item);
}
template<typename T>
void Set<T>::remove(const T& item)
{
typename std::list<T>::iterator it = std::find(rep.begin(), rep.end(), item);
if (it != rep.end())
rep.erase(it);
}
template<typename T>
std::size_t Set<T>::size() const
{
return rep.size();
}
請記渍曷鳌:
- 復(fù)合(composition)的意義和 public 繼承完全不同偶房。
- 在應(yīng)用域(application domain),復(fù)合意味著 has-a(有一個)军浆。在實(shí)現(xiàn)域(implementation domain)棕洋,復(fù)合意味 is-implementation-in-terms-of(根據(jù)某物實(shí)現(xiàn)出)。