Composition
约 1543 个字 213 行代码 预计阅读时间 15 分钟
组合 (Composition)
组合与命名空间
本章深入探讨C++中的组合机制和命名空间,组合是面向对象程序设计中重用实现的重要方式,通过已有对象构建新对象;命名空间则是控制名称作用域、避免命名冲突的机制。
组合的基本概念
- 组合:通过已有对象构建新对象的机制
- 表达的是"有一个"(has-a)的关系
- "每个对象的内存由其他对象组成" —— Alan Kay
对象包含的两种方式:
- 完全包含 (Fully)
- 意味着"它是这个对象的一部分"
- 引用包含 (By reference)
- 意味着"它在那里"
- 允许对象之间共享
Employee对象包含以下元素:
- 姓名(Name)
- 地址(Address)
- 健康计划(Health Plan)
- 薪资历史(Salary History):多个Raise对象的集合
- 主管(Supervisor):另一个Employee对象
组合实例
-
储蓄账户示例
-
内嵌对象机制
- 所有内嵌对象都会被初始化
- 如果不提供参数且存在默认构造函数,将调用默认构造函数
- 析构函数将自动调用,顺序与构造相反
- 可以用逗号分隔多个对象
- 为子构造函数提供参数
- 对象的初始化顺序与类中声明顺序一致,而非初始化列表中的顺序
不使用初始化列表的问题
如果不使用初始化列表,而是在构造函数体内使用设置方法:
SavingsAccount::SavingsAccount (
const char* name,
const char* address,
int cents ) {
m_saver.set_name(name);
m_saver.set_address(address);
m_balance.set_cents(cents);
}
问题:
- 会先使用默认构造函数初始化对象
- 然后在构造函数体内通过赋值修改状态
- 相当于进行了两次工作,效率较低
- 对于没有默认构造函数的类型无法使用此方法
公有与私有内嵌对象
- 通常将内嵌对象设为私有,作为底层实现的一部分
- 新类只公开内嵌对象的部分功能
- 实现封装,隐藏内部细节
完全包含与引用包含
-
完全包含
- "它在这里,作为这个对象的一部分"
- 内嵌对象的生命周期与外部对象绑定
- 构造函数和析构函数会自动调用
- 对象的内存布局包含所有内嵌对象
-
引用包含
- "它在那里",通过指针或引用访问
- 内嵌对象与外部对象生命周期可以不同
- 需要手动初始化和销毁对象
-
引用包含应用场景
引用包含通常应用于以下场景:
- 逻辑关系不是完全包含
- 初始时不知道对象大小
- 资源需要在运行时分配或连接
- 需要共享对象
- 需要动态变化的集合
其他面向对象语言(如Java)只使用引用包含,而C++两种方式都支持。
模块化与时钟显示实例
- 模块化是将整体划分为定义良好的部分的过程
- 这些部分可以单独构建和检查
- 并以明确定义的方式交互
初始化列表详解
- 可以初始化任何类型的数据
- 对于内置类型也进行伪构造函数调用
- 无需在构造函数体内执行赋值
- 初始化的顺序是按照成员变量声明的顺序,而不是初始化列表中的顺序
- 析构的顺序与初始化顺序相反
命名空间(Namespace)
-
基本概念
- 表达一组类、函数、变量等的逻辑分组
- 命名空间是一个作用域,类似于类
- 当只需要名称封装而不需要数据封装时使用
- 命名空间可以解决全局作用域中的名称冲突:
-
使用命名空间
-
命名空间高级特性
本章小结
- 组合是一种通过已有对象构建新对象的机制,表达"有一个"关系
- 组合有两种方式:完全包含和引用包含
- 初始化列表对于高效构造内嵌对象至关重要
- 命名空间用于逻辑分组和避免名称冲突
- 命名空间提供了灵活的名称管理机制,包括别名、组合和选择性引入