名字空间namespace
引自Effective C++
名字空间的概念加入到 C++标准的时间相对较晚,所以有些人会认为它不太重要,可有可无。但这种想法是错误的,因为C++标准库(参见条款49)里几乎所有的东西都存在于名字空间std 之中。这可能令你不以为然,但它却以一种直接的方式影响到你:这就是为什么C++提供了那些看起来很有趣的、没有扩展名的头文件,如<iostream>, <string>等。详细介绍参见条款49。
由于名字空间的概念引入的时间相对较晚,有些编译器可能不支持。就算是这样,那也没理由污染全局名字空间,因为可以用struct 来近似实现namespace。可以这样做:先创建一个结构用以保存全局符号名,然后将这些全局符号名作为静态成员放入结构中:
// 用于模拟名字空间的一个结构的定义
struct sdm {
static const double BOOK_VERSION;
class Handle { ... };
static Handle& getHandle();
};
const double sdm::BOOK_VERSION = 2.0; // 静态成员的定义
现在,如果有人想访问这些全局符号名,只用简单地在它们前面加上结构名作为前缀:
void f()
{
cout << sdm::BOOK_VERSION;
...
sdm::Handle h = sdm::getHandle();
...
}
但是,如果全局范围内实际上没有名字冲突,用户就会觉得加修饰符麻烦而多余。幸运的是,还是有办法来让用户选择使用它们或忽略它们。
对于类型名,可以用类型定义(typedef)来显式地去掉空间引用。例如,假设结构s(模拟的名字空间)内有个类型名T,可以这样用typedef 来使得T成为S::T 的同义词:
typedef sdm::Handle Handle;
对于结构中的每个(静态)对象X,可以提供一个(全局)引用X,并初始化为S::X:
const double& BOOK_VERSION = sdm::BOOK_VERSION;
老实说,如果读了条款 47,你就会不喜欢定义一个象BOOK_VERSION这样的非局部静态对象。(你就会用条款47 中所介绍的函数来取代这样的对象)
处理函数的方法和处理对象一样,但要注意,即使定义函数的引用是合法的,但代码的维护者会更喜欢你使用函数指针:
sdm::Handle& (* const getHandle)() = // getHandle 是指向sdm::getHandle
sdm::getHandle; // 的 const 指针 (见条款21)
注意 getHandle 是一个常指针。因为你当然不想让你的用户将它指向别的什么东西,而不是sdm::getHandle,对不对?
(如果真想知道怎么定义一个函数的引用,看看下面:
sdm::Handle& (&getHandle)() = // getHandle 是指向
sdm::getHandle; // sdm::getHandle 的引用
我个人认为这样的做法也很好,但你可能以前从没见到过。除了初始化的方式外,函数的引用和函数的常指针在行为上完全相同,只是函数指针更易于理解。)
有了上面的类型定义和引用,那些不会遭遇全局名字冲突的用户就会使用没有修饰符的类型和对象名;相反,那些有全局名字冲突的用户就会忽略类型和引用的定义,代之以带修饰符的符号名。还要注意的是,不是所有用户都想使用这种简写名,所以要把类型定义和引用放在一个单独的头文件中,不要把它和(模拟namespace 的)结构的定义混在一起。
struct 是namespace 的很好的近似,但实际上还是相差很远。它在很多方面很欠缺,其中很明显的一点是对运算符的处理。如果运算符被定义为结构的静态成员,它就只能通过函数调用来使用,而不能象常规的运算符所设计的那样,可以通过自然的中缀语法来使用:
// 定义一个模拟名字空间的结构,结构内部包含Widgets 的类型
// 和函数。Widgets 对象支持operator+进行加法运算
struct widgets {
class Widget { ... };
// 参见条款21:为什么返回const
static const Widget operator+(const Widget& lhs,
const Widget& rhs);
...
};
// 为上面所述的Widge 和operator+
// 建立全局(无修饰符的)名称
typedef widgets::Widget Widget;
const Widget (* const operator+)(const Widget&,const Widget&); // 错误! // operator+不能是指针名
Widget w1, w2, sum;
sum = w1 + w2; // 错误! 本空间没有声明
// 参数为Widgets 的operator+
sum = widgets::operator+(w1, w2); // 合法, 但不是 "自然"的语法
正因为这些限制,所以一旦编译器支持,就要尽早使用真正的名字空间。