图书目录

目  录

第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