《C++ API 设计》是一本非常不错的书,该书内容远远超过了它标题所指的 C++ API 设计,它不仅仅讲述了与编程语言无关的 API 设计需要遵循的一些规范,同时也介绍了很多 C++开发中需要遵守一些准则,如果在开发中能遵循这些准则,将有利于我们写出高质量的代码。
读罢该书,受益匪浅,本文将该书中我认为比较受用的部分摘录下来。
只有两种编程语言:一种是天天挨骂的,另一种是没人用的。
良好的 API 设计的首要目标是:在为客户提供所需功能的前提下,使用新发布的 API 对客户的代码造成的影响应该最小,理想的情况下应该是零影响。
C++ 没有包私有的概念,而是使用更加宽泛的友元的概念,以允许指定的类和方法访问某个类的受保护的和私有的成员。虽然友元可以用来加强封装,但是如果使用不当,它会向用户暴露过多的内部细节。
类的数据成员应该始终声明为私有的,而不是公有的或受保护的。
类只应该定义做什么,而不是怎么做。不要将某一个功能的具体实现步骤暴露出来,即便定义为私有的,也尽量不要这样做。可以使用
Pimpl
技巧来将所有的私有数据成员和函数隔离到一个.cpp 文件中独立实现的类或结构体内。不要过度承诺。当不确定是否需要某个接口时,就不要提供此接口。谨记奥卡姆(Occam)剃刀原理:若无必要,勿增实体。
谨慎添加虚函数,使用虚函数要意识到其潜在的隐患:
- 对基类看似无害的修改可能会给客户带来不利的影响。
- 客户可能会以你根本无法预料的方式来使用 API。
- 客户可能采用不正确的或易于出错的方式来扩展 API。
- 重写虚函数可能破坏类的内部原有逻辑的完整性。
使用虚函数要注意的方面:
- 虚函数的调用必须在运行时查虚函数表才能决定,无法在编译时决定。
- 使用虚函数需要维护虚函数表指针,进而增加对象的大小。
- 添加、重排或者移除虚函数会破坏二进制兼容性。因为虚函数调用通常用类的虚函数表的整型偏移量来表示,所以改变虚函数的顺序,或者执行可能会引起其他虚函数的顺序发送变化的操作,都需要重新编译现有代码。
- 如果类包含了任一虚函数,那么必须将析构函数声明为虚函数。这样子类就可以释放其可能申请的额外资源。
- 绝不在构造函数或析构函数中调用虚函数,这些调用不会指向之类。
Sutter 建议接口应该是非虚的,同时在适当的情况下使用模板方法设计模式。
给出清晰的、描述性强的且恰当的名字是 API 设计中最困难的任务之一。
避免编写拥有多个相同类型参数的函数。
使用一致的(统一规范)函数命令和参数顺序。
API 接口应该是平台独立的。不要将平台相关的
#if
或#ifdef
语句放在公共的 API 中,因为这些语句暴露了实现的细节,并使 API 因平台而异。优秀的 API 表现为松耦合和高内聚。
除非确实需要
#include
类的完整定义,否则应该为类使用前置声明。通常,优秀的软件工程实践的目标是去除冗余,即确保每个重要的知识点或行为有且仅有一次实现。而代码复用意味着耦合,因此略微的增加重复以断绝过分的耦合关系有时是值得的。
《C++ API 设计》[美] Martin Reddy 著;刘晓娜,臧秀涛,林健 译