复合是类型之间的一种关系,当某种类型的对象内含其他类型的对象,便是这种关系。例如:
class Address {...};
class PhoneNumber {...};
class Person {
public:
std::string name; // 合成成分
Address address;
PhoneNumber voiceNumber;
PhoneNumber faxNumber;
};
上述Person类示范has-a关系。Person有一个名字,一个地址,以及语音传真和电话号码。
对于is-implemented-in-terms-of(根据某物表现出)对象关系,假设你需要一个template,希望制造出一组class用来表现由不重复对象组成的set,而标准库set的实现会导致每个元素耗用三个指针的额外开销。实现set的方法还可以使用标准库list,更明确地说,你决定让你自己实现的Set template继承 std::list
:
template <typename T>
class Set : public std::list<T> {...};
但根据条款32,如果D是一种B,对B为真的每一件事对D也都应该为真。但list内可含重复元素,而Set不能含有重复元素,因此Set是一种list并不为真。
由于这两个class之间并非is-a关系,所以public继承不适合来建模它。正确的做法是,Set对象可根据一个list对象实现出来:
template <class 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成员函数可大量依赖list及标准库的其他完成功能,所以其实现也很直观简单:
template <typename T>
bool Set<T>::number(const T& item) {
return std::find(rep.begin(), rep.end(), item) != rep.end();
}
template <typename T>
void Set<T>::insert(const T& item) {
if (!number(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(); }
这样,Set通过list表现出来。
请记住
- 复合的意义和public继承完全不同。
- 在应用域,复合意味着has-a。在实现域,复合意味着is-implemented-in-terms-of。