陇南市网站建设_网站建设公司_Vue_seo优化
2025/12/17 20:23:47 网站建设 项目流程

我们之前的 Item(如 32, 34, 36)都在讨论 Public Inheritance(公有继承),它的核心意义是 "Is-a"(是一个) 的关系。 而 Composition(复合/组合) ——即一个类包含另一个类型的对象作为成员变量——则代表了完全不同的两种意义:

  1. Has-a(拥有):应用域(Application Domain)的概念。
  2. Is-implemented-in-terms-of(根据某物实现出):实现域(Implementation Domain)的概念。

以下是深度解析。


1. 什么是复合 (Composition)?

非常简单,就是一个类里面有另一个类的对象。

class Address { ... };
class PhoneNumber { ... };class Person {
public:...
private:std::string name;        // CompositionAddress address;         // CompositionPhoneNumber voiceNumber; // CompositionPhoneNumber faxNumber;   // Composition
};

在这个例子中,Person 是由 string, Address, PhoneNumber 等对象组成的。这就是复合。


2. 第一层含义:Has-a(拥有)

这是最直观的含义,通常出现在应用域(即你正在建模的现实世界业务逻辑)中。

  • 逻辑: 人(Person)拥有一个名字(Name)。人拥有一个地址(Address)。
  • 反例: 你绝不会说“人是一个地址”(Person is an Address)。因此,Person 不应该继承 Address,而应该包含它。

这一点大多数人都不会搞错。


3. 第二层含义:Is-implemented-in-terms-of(根据某物实现出)

这是实现域(即纯粹为了写代码、数据结构、算法)中的概念。这也是最容易误用继承的地方。

场景案例:我们需要一个 Set(集合)

假设你需要实现一个 Set 模板类。你知道 Set 的特性是:

  • 元素不能重复。
  • 通常无序(或者是特定的数学顺序)。
  • 需要空间存储元素。

你手里正好有一个现成的、功能强大的 std::list。你想:“太好了,我可以复用 std::list 的代码来存储数据。”

❌ 错误的各种做法:使用 Public Inheritance

// 错误设计:让 Set 继承 List
template<typename T>
class Set : public std::list<T> { ... };

这就犯了 Item 32 ("Public inheritance means is-a") 的大忌。 如果 Set 继承了 List,那么 Set 就是一个 List。这就意味着适用于 List 的所有操作必须适用于 Set

  • List 可以包含重复元素。Set 不行。
  • List 可以在指定位置插入 (insert)。Set 通常不需要关心物理位置。
  • List 可以拼接 (splice)。

如果用户写了这行代码:

Set<int> s;
s.push_back(10); // 第一次
s.push_back(10); // 第二次!List 允许这样做,但 Set 不应该允许!

此时你的 Set 就破功了。因为它继承了 List 的接口,导致它的行为不再像一个 Set

✅ 正确的做法:使用 Composition

我们应该说:Set 是根据 List 实现出来的(Set is implemented in terms of List)。 Set 使用 List 来管理内存和数据,但 Set 不是 一个 List

template<typename T>
class Set {
public:// 只有 Set 该有的接口bool member(const T& item) const;void insert(const T& item);void remove(const T& item);std::size_t size() const;private:// 内部实现细节:使用 list 来干活std::list<T> rep; 
};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); // 复用 list 的功能}
}

为什么这样更好?

  1. 接口隔离Set 的用户看不到 std::listpush_frontsplice 等不适合 Set 的函数。
  2. 封装性:以后如果你发现 std::list 性能不好,想换成 std::vector 或自定义的哈希表,你只需要修改 Set 的内部实现 (private 部分),外部使用 Set 的代码完全不需要改动。

4. 总结

  • Public 继承:意味着 "Is-a"(是一个)。除此之外,别用它。
  • Composition(复合):意味着 "Has-a"(拥有)或 "Is-implemented-in-terms-of"(根据某物实现出)。
    • 如果是现实世界的对象关系(如人与地址),它是 Has-a。
    • 如果是纯粹的代码复用(如用 List 实现 Set),它是 Is-implemented-in-terms-of。

在设计类的关系时,优先考虑组合,而不是继承

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询