Skip to content

Latest commit

 

History

History
70 lines (57 loc) · 2.28 KB

File metadata and controls

70 lines (57 loc) · 2.28 KB

条款38:通过复合建模出has-a或“根据某物实现出”

复合是类型之间的一种关系,当某种类型的对象内含其他类型的对象,便是这种关系。例如:

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。