作者
DebHaldar
译者
姜松浩,责编
屠敏
对于许多C++开发人员来说,API设计可能会在其优先级列表中排名第3或第4。大多数开发人员都倾向于使用C++来获得原始功能和控制权。因此,性能和优化的想法占据这些开发者的时间的百分之八十。
当然,每个C++开发人员都会考虑头文件设计的各个方面,但是API设计不仅仅是头文件设计那样。事实上,我强烈建议每一个开发人员在其API的设计上,无论是面向公共还是面向内部,都给予一些帮助,因为这样可以节省你大量的维护成本,提供平滑的升级路径,并为你的客户节省麻烦。
下面列出的许多错误都是我自己的经验和我从MartinReddy的精彩书籍《C++APIDesign》(我强烈推荐的书)中学到的东西的结合。如果你真的想要深入了解C++API设计,那么你应该阅读MartinReddy的书,然后使用下面的列表作为更多的清单来强制执行代码审查。
错误#1:不将你的API放在命名空间中
为什么这是一个错误?
因为你不知道将使用哪个代码库,特别是对于外部API。如果不将API功能限制在命名空间中,则可能导致与该系统中使用的其他API发生名称冲突。
例如:
让我们考虑一个非常简单的API和使用它的客户端类:
//API-InLocation.hclassvector{public:vector(doublex,doubley,doublez);private:doublexCoordinate;doubleyCoordinate;doublezCoordinate;};//ClientProgram#includestdafx.h#includeLocation.h#includevectorusingnamespacestd;intmain(){vectorintmyVector;myVector.push_back(99);return0;}
如果有人试图在同时使用std::vector的项目中使用这个类,他们会得到一个错误“errorC:‘vector’:ambiguoussymbol”。这是因为编译器无法决定客户端代码引用的向量是std::vector还是location.h中定义的vector对象。
如何解决这个问题?
始终将API放在自定义命名空间中,例如:
//APInamespaceLocationAPI{classvector{public:vector(doublex,doubley,doublez);private:doublexCoordinate;doubleyCoordinate;doublezCoordinate;};}
另一种方法是为所有公共API符号添加一个唯一的前缀。如果遵循此约定,我们将调用我们的类“lvector”而不是“vector”。此方法用于OpenGL和QT。
在我看来,如果你正在开发纯C的API,这是有道理的。确保所有公共符号符合此唯一命名约定是另一个令人头痛的问题。如果你正在使用C++,那么你应该只在命名空间中对API功能进行分组,让编译器为你完成繁重的任务。
我还强烈建议你使用嵌套命名空间来进行功能分组或将公共API与内部API分开。一个很好的例子是Boost库,它们可以自由地使用嵌套的命名空间。例如,在根“boost”命名空间内,boost::variant包含BoostVariantAPI的公共符号,boost::detail::variant包含该API的内部详细信息。
错误#2:在你的公共API头的全局范围中包含“usingnamespace”
为什么这是一个错误?
这将导致被引用命名空间中的所有符号在全局命名空间中变得可见,并首先抵消掉使用命名空间的好处。
另外:
头文件的使用者不可能撤消命名空间包含,因此他们被迫使用决策来使用你的命名空间,这是不可取的。它极大地增加了命名空间首先要解决的冲突的可能性。当引入新版本的库时,程序的工作版本可能无法编译。如果新版本引入的名称与应用程序正在从另一个库使用的名称冲突,则会发生这种情况。代码中的“usingnamespace”部分从包含头部的代码中出现的那一点开始生效,这意味着在此之前出现的任何代码都可能与该点之后出现的任何代码区别对待。如何解决这个问题?
1.尽量避免在头文件中放置任何使用的命名空间声明。如果你需要一些名称空间对象来编头文件,请在头文件中使用完全限定名称(例如std::cout,std::string)。
//File:MyHeader.h:classMyClass{private:Microsoft::WRL::ComPtr_parent;Microsoft::WRL::ComPtr_child;}
2.如果上面的建议#1导致代码混乱太多-将“usingnamespace”用法限制在头文件中定义的类或命名空间内。另一个选择是在头文件中使用范围别名,如下所示。
//File:MyHeader.h:classMyClass{namespacewrl=Microsoft::WRL;//notethealiasinghere!private:wrl::ComPtr_parent;wrl::ComPtr_child;}
有关与C++头文件相关的其他问题,请参阅帖子“十大C++头文件错误以及如何修复它们”(