目 录
第1章 概述 1
1.1 什么是API 1
1.1.1 契约和承包商 2
1.1.2 C++中的API 2
1.2 API设计有什么不同 4
1.3 为什么使用API 6
1.3.1 更健壮的代码 6
1.3.2 代码重用 7
1.3.3 并行开发 8
1.4 何时应该避免使用API 10
1.5 API示例 11
1.5.1 API层次 11
1.5.2 真实示例 14
1.6 库、框架和软件开发工具包 15
1.7 文件格式和网络协议 16
1.8 关于本书 17
第2章 特性 20
2.1 问题域建模 20
2.1.1 提供良好的抽象 20
2.1.2 关键对象建模 22
2.1.3 解决核心问题 24
2.2 隐藏实现细节 25
2.2.1 物理隐藏:声明与定义 25
2.2.2 逻辑隐藏:封装 27
2.2.3 隐藏成员变量 28
2.2.4 隐藏实现方法 31
2.2.5 隐藏实现类 33
2.3 最小完整性 35
2.3.1 不要过度承诺 35
2.3.2 拒绝重复 36
2.3.3 便利的API 37
2.3.4 谨慎添加虚函数 39
2.4 易于使用 41
2.4.1 可发现性 41
2.4.2 难误用 42
2.4.3 一致性 44
2.4.4 正交 46
2.4.5 稳健的资源分配 49
2.4.6 平台独立 52
2.5 松耦合 53
2.5.1 仅通过名称耦合 54
2.5.2 降低类耦合度 55
2.5.3 刻意的冗余 56
2.5.4 管理类 58
2.5.5 回调、观察者和通知 60
2.6 稳定、文档齐全且经过测试 65
第3章 模式 67
3.1 pimpl惯用法 69
3.1.1 使用pimpl 69
3.1.2 复制语义 73
3.1.3 pimpl和智能指针 73
3.1.4 pimpl的优势 74
3.1.5 pimpl的缺点 75
3.1.6 C语言中的不透明指针 76
3.2 单例(Singleton) 78
3.2.1 在C++语言中实现单例 79
3.2.2 单例和线程安全 80
3.2.3 单例与依赖注入 81
3.2.4 单例模式与共享状态模式 82
3.2.5 单例与会话状态 84
3.3 工厂方法 84
3.3.1 抽象基类和接口 85
3.3.2 简单工厂方法示例 86
3.3.3 可扩展的工厂方法示例 88
3.4 API包装模式 90
3.4.1 代理模式 91
3.4.2 适配器模式 93
3.4.3 外观模式 95
3.5 观察者模式 97
3.5.1 模型—视图—控制器 97
3.5.2 观察者模式的实现 98
3.5.3 推送和拉取观察者 101
第4章 设计 103
4.1 良好的设计 104
4.1.1 技术债的积累 104
4.1.2 偿还技术债 106
4.1.3 长期设计 107
4.2 收集功能性需求 108
4.2.1 什么是功能性需求 108
4.2.2 功能性需求示例 109
4.2.3 维护需求 110
4.3 创建用例 110
4.3.1 开发用例 110
4.3.2 用例模板 111
4.3.3 编写高质量用例 112
4.3.4 需求和敏捷开发 113
4.4 API的设计要素 115
4.5 架构设计 117
4.5.1 架构开发 118
4.5.2 架构的约束 119
4.5.3 识别主要抽象概念 121
4.5.4 构建关键对象 122
4.5.5 架构模式 124
4.5.6 针对架构的沟通 126
4.6 类设计 127
4.6.1 面向对象概念 127
4.6.2 类设计选项 128
4.6.3 SOLID原则 129
4.6.4 使用继承 129
4.6.5 里氏替换原则 131
4.6.6 开放—封闭原则 134
4.6.7 迪米特法则 135
4.6.8 类命名 136
4.7 函数设计 137
4.7.1 函数设计选项 137
4.7.2 函数命名 138
4.7.3 函数参数 139
4.7.4 错误处理 142
第5章 风格 147
5.1 扁平式C API 148
5.1.1 ANSI C特性 149
5.1.2 ANSI C API的优点 150
5.1.3 使用ANSI C编写API 151
5.1.4 从C ++调用C函数 152
5.1.5 案例研究:FMOD C API 153
5.2 面向对象的C++ API 154
5.2.1 面向对象的API的优点 154
5.2.2 面向对象的API的缺点 155
5.2.3 案例研究:FMOD C++ API 155
5.3 基于模板的API 156
5.3.1 基于模板的API示例 157
5.3.2 模板和宏 157
5.3.3 基于模板的API的优点 158
5.3.4 基于模板的API的缺点 159
5.4 函数式API 160
5.4.1 函数式编程概念 160
5.4.2 函数式API示例 161
5.4.3 函数式API的优点 162
5.4.4 函数式API的缺点 162
5.5 数据驱动的API 163
5.5.1 数据驱动API的优点 164
5.5.2 数据驱动API的缺点 166
5.5.3 支持可变参数列表 166
5.5.4 案例研究:FMOD数据驱动的API 167
5.5.5 数据驱动的Web服务 168
5.5.6 幂等性 168
第6章 C++用法 172
6.1 命名空间 172
6.2 构造函数和赋值运算符 174
6.2.1 定义构造函数和赋值运算符 176
6.2.2 explicit关键字 177
6.3 const正确性 178
6.3.1 方法的const正确性 178
6.3.2 参数的const正确性 180
6.3.3 返回值的const正确性 180
6.4 模板 182
6.4.1 模板术语 182
6.4.2 隐式实例化API设计 184
6.4.3 显式实例化API设计 185
6.5 运算符重载 187
6.5.1 可重载的运算符 188
6.5.2 自由运算符和成员运算符 189
6.5.3 向类中添加运算符 190
6.5.4 运算符语法 192
6.5.5 转换运算符 195
6.6 函数形参 196
6.6.1 指针和引用形参 196
6.6.2 默认实参 197
6.7 避免使用#define来定义常量 198
6.8 避免使用友元 200
6.9 导出符号 202
6.10 编码规范 205
第7章 C++版本 208
7.1 使用哪个C++版本 208
7.2 C++11 API特性 209
7.2.1 移动构造函数和五法则 209
7.2.2 默认和删除函数 213
7.2.3 对象构造 215
7.2.4 初始化器列表构造函数 217
7.2.5 智能指针 218
7.2.6 枚举类 220
7.2.7 override和final说明符 221
7.2.8 noexcept说明符 225
7.2.9 内联命名空间 225
7.2.10 使用using的类型别名 227
7.2.11 用户定义字面量 228
7.2.12 后置返回类型声明 230
7.2.13 元组 230
7.2.14 常量表达式 231
7.2.15 关键字nullptr 233
7.2.16 变参模板 233
7.2.17 迁移至C++11 235
7.3 C++14 API特性 237
7.3.1 auto返回类型 237
7.3.2 废弃属性 238
7.3.3 变量模板 239
7.3.4 常量表达式的改进 240
7.3.5 二进制字面量和数字分隔符 241
7.3.6 迁移至C++14 241
7.4 C++17 API特性 242
7.4.1 内联变量 242
7.4.2 字符串视图 243
7.4.3 optional 245
7.4.4 any 248
7.4.5 variant 249
7.4.6 嵌套命名空间 251
7.4.7 折叠表达式 252
7.4.8 头文件的可用性检查 254
7.4.9 字节类型 255
7.4.10 maybe_unused属性 256
7.4.11 迁移至C++17 257
7.5 C++20 API的特性 258
7.5.1 模块 258
7.5.2 太空船运算符 261
7.5.3 约束和概念 265
7.5.4 简写函数模板 267
7.5.5 consteval说明符 268
7.5.6 constinit说明符 269
7.5.7 迁移至C++20 269
7.6 C++23 API的特性 271
7.6.1 期值 271
7.6.2 多维下标运算符 272
7.6.3 预处理器指令 273
7.6.4 迁移至C++23 275
第8章 性能 276
8.1 通过const引用传递输入参数 277
8.2 最小化#include依赖 279
8.2.1 避免“无所不包型”头文件 279
8.2.2 前置声明 279
8.2.3 冗余的#include保护 281
8.3 声明常量 284
8.4 初始化列表 286
8.5 内存优化 288
8.6 若非必要,勿用内联 293
8.7 写时复制 295
8.8 遍历元素 299
8.8.1 迭代器 299
8.8.2 随机访问 300
8.8.3 数组引用 301
8.8.4 外部模板 302
8.9 性能分析 303
8.9.1 时效性分析 303
8.9.2 基于内存的分析 306
8.9.3 多线程分析 307
第9章 并发 308
9.1 C++中的多线程 308
9.2 术语 310
9.2.1 数据竞争和竞争条件 310
9.2.2 线程安全 310
9.2.3 可重入性 311
9.2.4 异步任务 312
9.2.5 并行 312
9.3 访问共享数据 313
9.3.1 无状态API 313
9.3.2 初始化共享数据 315
9.3.3 同步数据访问 316
9.4 并发API设计 317
9.4.1 并发的最佳实践 318
9.4.2 线程安全接口模式 319
第10章 版本控制 321
10.1 版本号 321
10.1.1 版本号的意义 321
10.1.2 特殊的编号方案 322
10.1.3 创建版本API 323
10.2 软件的分支策略 325
10.2.1 分支方案 325
10.2.2 分支原则 326
10.2.3 API和并行分支 326
10.2.4 文件格式和并行发布产品 327
10.3 API的生命周期 328
10.4 兼容性级别 329
10.4.1 向后兼容性 329
10.4.2 功能兼容性 330
10.4.3 源码兼容性 330
10.4.4 二进制兼容性 331
10.4.5 向前兼容性 334
10.5 如何维护向后兼容性 335
10.5.1 添加功能 335
10.5.2 修改功能 336
10.5.3 弃用功能 337
10.5.4 移除功能 339
10.5.5 用于版本控制的内联命名空间 340
10.6 API审查 341
10.6.1 API审查的目的 341
10.6.2 发布前API审查 342
10.6.3 提交前的API审查 344
第11章 文档 346
11.1 撰写文档的理由 346
11.1.1 定义行为 347
11.1.2 为接口契约编写文档 348
11.1.3 告知行为的改变 349
11.1.4 文档涉及的内容 350
11.2 文档的类型 352
11.2.1 自动生成的API文档 353
11.2.2 概述文档 353
11.2.3 示例和教程 354
11.2.4 发行说明 355
11.2.5 许可证信息 355
11.3 文档可用性 358
11.4 使用Doxygen 361
11.4.1 配置文件 361
11.4.2 注释风格和命令 361
11.4.3 API注释 363
11.4.4 文件注释 364
11.4.5 类注释 365
11.4.6 方法注释 365
11.4.7 枚举注释 366
第12章 测试 369
12.1 编写测试的原因 369
12.2 API测试的类型 371
12.2.1 单元测试 372
12.2.2 集成测试 375
12.2.3 性能测试 376
12.3 编写良好的测试 378
12.3.1 良好测试的特征 378
12.3.2 测试对象 379
12.3.3 关注测试工作量 381
12.3.4 与QA一起工作 382
12.4 编写可测试的代码 382
12.4.1 测试驱动开发 382
12.4.2 桩对象和模拟对象 385
12.4.3 测试私有代码 388
12.4.4 使用断言 391
12.4.5 契约编程 392
12.4.6 记录和回放功能 395
12.4.7 支持国际化 396
12.5 自动化测试工具 396
12.5.1 自动化测试框架 397
12.5.2 代码覆盖率工具 400
12.5.3 缺陷跟踪系统 403
12.5.4 持续构建系统 404
第13章 Objective-C和Swift 405
13.1 C++和Objective-C中的接口设计 405
13.2 Objective-C中的数据隐藏 407
13.3 C++ API背后的Objective-C 409
13.4 Objective-C API背后的C++ 412
13.5 Swift API背后的C++ 414
第14章 脚本化 418
14.1 添加脚本绑定 418
14.1.1 扩展与嵌入 418
14.1.2 脚本化的优势 419
14.1.3 语言兼容性问题 420
14.1.4 跨越语言障碍 421
14.2 脚本绑定技术 422
14.2.1 Boost Python 422
14.2.2 简化的包装器和接口生成器 423
14.2.3 Python-SIP 423
14.2.4 组件对象模型自动化 424
14.2.5 CORBA 425
14.3 使用Boost Python添加Python绑定 425
14.3.1 构建Boost Python 426
14.3.2 使用Boost Python包装C++ API 427
14.3.3 构造函数 429
14.3.4 扩展Python API 430
14.3.5 C++中的继承 432
14.3.6 跨语言多态性 433
14.3.7 支持迭代器 434
14.3.8 综合应用 435
14.4 使用SWIG添加Ruby绑定 437
14.4.1 使用SWIG包装C++API 438
14.4.2 调试Ruby API 439
14.4.3 构造函数 440
14.4.4 扩展Ruby API 441
14.4.5 C++中的继承 442
14.4.6 跨语言多态性 444
14.4.7 综合应用 444
第15章 可扩展性 447
15.1 通过插件扩展 447
15.1.1 插件模型概述 448
15.1.2 插件系统的设计问题 450
15.1.3 使用C++实现插件 452
15.1.4 插件API 453
15.1.5 插件示例 455
15.1.6 插件管理器 456
15.1.7 插件版本控制 458
15.2 通过继承扩展 460
15.2.1 添加功能 460
15.2.2 修改功能 461
15.2.3 继承与标准库 462
15.2.4 继承和枚举 463
15.2.5 访问者模式 464
15.3 通过模板扩展 469
15.3.1 基于策略的模板 469
15.3.2 奇异递归模板模式 470
附录 库 473
