我们或许希望所定义的对象、函数、类类型或其他实体,它只在程序的一小段代码中可见,因为这样可以更进一步地缓解名字空间污染问题,因为我们知道该实体只被用在很有限的地方,所以可能不想再花费太多努力来保证这个实体有惟一的名字而不会与程序其他地方声明的名字冲突。当我们在一个函数或嵌套块中声明一个对象时,由该声明引入的名字只在声明它的块中可见,但是,如果程序员想让一个实体被多个函数使用,而又不想让该名字在整个程序中可用 又该怎么办呢?
例如 假设我们想实现一组排序函数,对 double 型 vector 的元素进行排序:
// ----- SortLib.h ----- void quickSort( double *, double * ); void bubbleSort( double *, double * ); void mergeSort( double *, double * ); void heapSort( double *, double * );
所有函数都使用同一个 swap()函数来交换vector 中的元素,但是,我们不想让 swap()在整个程序中可见 我们希望保持该函数对于文件SortLib.C的局部性,因为只有上面四个函数调用swap()。下面的代码没有给我们预期的结果,你能看出为什么吗 ?
// ----- SortLib.C ----- void swap( double *d1, double *d2 ) { /* ... */ } // 只有下面四个函数使用 swap() void quickSort( double *d1, double *d2 ) { /* ... */ } void bubbleSort( double *d1, double *d2 ) { /* ... */ } void mergeSort( double *d1, double *d2 ) { /* ... */ } void heapSort( double *d1, double *d2 ) { /* ... */ }
即使函数 swap()在 SortLib.C中定义,并且没有在描述排序库的接口的头文件SortLib.h中引入。但是,函数 swap()仍然是在全局域中声明的,因此它是一个全局实体,它的名字不能与任何其他全局实体的名字冲突 。
在C++中 我们可以用未命名的名字空间 unnamed namespace 声明一个局部于某一文件的实体 ,未命名的名字空间以关键字 namespace开头,同为该名字空间是没有名字的,所以在关键字 namespace后面没有名字,而在关键字 namespace后面使用花括号包含声明块
// ----- SortLib.C ----- namespace { void swap( double *d1, double *d2 ) { /* ... */ } } // 上面四个排序函数的定义
函数 swap()只在文件 SortLib.C中可见,如果另一个文件也含有一个带有函数swap()定义的未命名名字空间,则该定义引入的是一个不同的函数,函数 swap()存在两种定义但这并不是个错误,因为它们是不同的函数,不像其他名字空间,未命名的名字空间的定义局部于一个特定的文件,不能跨越多个文本文件 。
在 SortLib.C中,在未命名的名字空间的定义之后,我们可以用 swap()的简短格式引用它,没有必要用域操作符引用未命名名字空间的成员 。
void quickSort( double *d1, double *d2 ) { // ... double* elem = d1; // ... // 引用未命名名字空间成员 swap() swap( d1, elem ); // ... }
由于未命名名字空间的成员是程序实体,所以函数 swap()可以在程序整个执行期间被调用,但是,未命名名字空间成员名只在特定的文件中可见 在构成程序的其他文件中是不可见的 。
在引入标准 C++名字空间之前,解决此类声明局部化问题的常见方案是使用从 C语言中继承来的关键字 static,未命名名字空间的成员与被声明为static 的全局实体具有类似的特性 。在 C中 被声明为static 的全局实体在声明它的文件之外是不可见的 ,例如 在 SortLib.C中的声明可以按如下形式写成 C程序,它会提供给 swap()相同的特性 。
// SortLib.C // swap() 在其他程序中不可见 static void swap( double *d1, double *d2 ) { /* ... */ } // sort 函数定义同前
许多 C++实现都支持全局静态声明,但是,随着越来越多的 C++实现都支持名字空间全局静态声明的用法将会被未命名的名字空间成员所取代。