我最近做了一堆Java编码,已经习惯了非常具体的包命名系统,例如深度嵌套com.company.project.db
.这在Java,AS3/Flex和C#中工作正常.我也看到了在C++中应用的相同范例,但我也听说将C++命名空间视为Java包的直接对应物是不好的.
这是真的,为什么?命名空间/包如何相似和不同?如果使用深层嵌套命名空间,可能会出现哪些问题?
在C++命名空间中,只是分区可用的名称.Java包是关于模块的.命名层次结构只是它的一个方面.
本质上没有任何错误,在C++中具有深层嵌套的命名空间,除了它们通常不是必需的,因为它们后面没有模块系统,而额外的层只会增加噪声.拥有一个或两个级别的命名空间通常就足够了,内部细节的奇数额外级别(通常称为详细信息).
C++命名空间还有一些额外的规则,如果过度使用可能会引起你的注意 - 例如参数依赖查找,以及解决父级别的规则.WRT后者,采取:
namespace a{ namespace b{ int x; } } namespace b{ string x; } namespace a { b::x = 42; }
这合法吗?发生了什么事情很明显?您需要知道命名空间解析的优先级才能回答这些问题.
Java包不是嵌套的,它们是扁平的.任何明显的嵌套只不过是命名约定.
例如,该包com.company.project.db
具有任何没有关系com.company.project
或com.company.project.db.x
.Code in com.company.project.db
无法访问代码,而com.company.project.db.x
不是代码a.b.c
.
以下是C ++名称空间与Java或C#名称空间不同的原因。
避免冲突
在Java / C#语言中,名称空间旨在避免类库不同部分中的名称之间发生冲突。您可能在C#的名称空间层次结构中的5个不同位置拥有名为“ Watcher”的类。在C ++中,如果库中出现相同的命名类,则将其放置在另一个类中而不是创建名称空间。这样的嵌套类都很好并且受到鼓励,实际上,语法也将类视为使用::运算符的名称空间。
嵌套的命名空间应该是多少?
流行的库如Boost,Eigen以及STL当然提供了很好的例子。这些库通常会将几乎所有东西都塞进一个命名空间中,例如std::
or boost::
或or eigen::
。很少有组件能够像std::ios
或那样拥有自己的名称空间boost:filesystem
。关于何时使用第二层级没有统一的规则,但是看起来大型,单独开发/维护或可选组件通常具有自己的名称空间。第三级甚至更罕见。通常,我使用项目的结构company::project
和大型可独立使用的子系统company::project::component
。
避免外部库中的冲突
现在有个大问题:如果您从两个不同的人那里获得两个具有完全相同的名称空间和类的库,该怎么办?这种情况相当罕见,因为大多数人倾向于至少以他们的项目名称包装他们的库。即使项目名称相同,您最终使用这两个库的情况更为罕见。但是,有时会为项目名称做出错误的决定(嗯……“地铁”,“阿波罗”……),甚至根本不使用名称空间。如果发生这种情况,您可以将其中一个或两个库的#include包装到名称空间中,即可解决冲突!这是人们不为冲突而烦恼太多的原因之一,因为解决冲突是微不足道的。如果您遵循使用惯例,company::project
那么冲突将变得极为罕见。
语言差异
尽管C ++提供的using namespace
语句与C#一样,但通常将其“导入”您自己的命名空间中的所有内容视为一种不好的做法。这样做的原因是,标题可能包含许多“不良”内容,包括重新定义使您完全感到惊讶的内容。这与C#/ Java完全不同,在C#/ Java中,当您执行等同于时,您只会获得干净的公共接口using namespace
。(旁注:在C ++中,您可以通过使用Pimpl模式来实现相同的功能,但是通常它会产生过多的额外负担,实际上很少有库会这样做)。因此,您几乎永远不想做using namespace
。相反,您为实际要使用的内容做typedefs
(或using name =
)。这再次使深度嵌套的名称空间无法使用。
组织代码
在Java / C#中,人们倾向于在文件夹中组织很多代码。通常,随着文件夹增长超过20个甚至10个文件,人们会开始考虑文件夹。在C ++中,情况更加多样化,但是对于许多大型项目而言,更平坦的目录结构是首选。例如,标准库的std文件夹有53个文件,而Facebook的愚蠢文件项目似乎走了同样的路。我认为,造成这种情况的一个原因可能是Java / C#人们更多地使用可视化IDE,并且在文件夹导航中使用了鼠标滚动,而不是在控制台中可以使用通配符在平面结构中查找文件的控制台。同样,C ++程序员绝对不要回避在单个文件中放置多个类,并将文件命名为逻辑单元,而不是与C#或Java中的类名相同。这样可以加快编译速度,这对于大型项目非常重要。尽管没有语言级别的要求,每个文件夹都拥有自己的名称空间,但许多C ++开发人员更喜欢为每个文件夹分配其自己的名称空间,并保持文件夹层次结构为2级或更少。
可能的例外
在C ++中A::B::C::D
,只要C::D
您已经在内部,就可以引用它A::B
。因此,如果您想进一步降低私有代码或较少使用的类,则可以这样做,同时仍将自己的相对深度保持在2左右。在这种情况下,您可能还想为每个级别创建文件夹,以便文件位置是可预测的。通常,该领域没有黄金标准,但是您不希望过度模仿C#/ Java的深度嵌套名称空间。
有关
避免头文件中的冲突;使用名称空间包装器?
是否有“正确”的方式在C ++中处理名称空间
在C ++中使用完全限定的名称
Java软件包与C ++库
是否有更好的方法在标头中以C ++表示嵌套的名称空间
您如何在C ++中正确使用名称空间?