博客 阅读时间 3 分钟

C++ 项目中使用命名空间的 5 个常见原因

分享本文
5 Common Reasons for Using Namespaces in C++ Projects

命名空间于 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中。
分享本文