命名空间于 1995 年被引入 C++ 标准,通常这样定义:
命名空间定义了一个新的作用域,并提供了一种避免名称冲突的方式。
C++ 中的命名空间最常用于避免命名冲突。尽管命名空间在近期的 C++ 代码中被广泛使用,但大多数较老的代码并不使用这一机制。
在研究了许多 C++ 项目的源代码之后,以下是这些项目中使用命名空间的一些常见原因。
1. 避免名称冲突。
如上所述,这是最常见的原因。在这种情况下,使用命名空间主要是为了方便编译器;它并不一定能提高代码对开发者的可读性或可维护性。
2. 模块化应用程序
现代 C++ 库大量使用命名空间来模块化其代码库,它们采用"按特性划分命名空间"(namespace-by-feature)的方式。这种方式使用命名空间来反映特性集,把与单个特性(且仅与该特性)相关的所有项放入同一个命名空间。这样产生的命名空间具有高内聚性和高模块性,命名空间之间的耦合最小。紧密协作的项被放在一起。
Boost 是按特性分组的最佳例子;它包含数千个命名空间,每个命名空间用于组合一个特定的特性。
3. 匿名命名空间。
匿名命名空间提供了全局静态变量的替代方案。匿名命名空间只在声明它的文件内可访问。
4. 枚举问题的变通方案。
C++ 中的"传统"枚举会把其枚举值导出到外围作用域,如果同一作用域中的两个不同枚举定义了同名的枚举值,就可能导致名称冲突。
在大型项目中,无法保证两个不同的枚举不会使用相同的枚举值名称。这个问题在 C++11 中通过enum class得到了解决,它隐式地将枚举值限定在枚举名称的作用域内。
在 C++11 之前,常见的变通方法是把枚举声明在命名空间内。例如,不要像这样声明枚举:
enum status{
status_ok,
status_error
};而是可以把它声明在命名空间内:
namespace status{
enum status{
ok,
error
};
}许多 C++ 项目使用这个技巧;例如,虚幻引擎的源代码就广泛使用了这种技术。
5. 按约定隐藏细节
对于代码在头文件中实现的模板库,向库使用者表明某些类型属于实现细节、不应直接使用,是很有用的。在 C# 中,"internal"关键字就起到这个作用,但在 C++ 中没有等价的方法来对库使用者隐藏公有类型。
现代 C++ 中一个常见的惯用法——由 Boost 库的开发者开创——是把属于模块实现一部分(即不属于公共 API)但又必须公开可用的符号,分离到一个按约定命名为 detail 的独立子命名空间中。
例如,Boost.Math 的文档指出:
不打算供应用程序使用的函数位于boost::math::detail中。