作者 | 梁唐
大家好,我是梁唐。
这是EasyC++系列的第51篇,来聊聊一个很重要的概念——名称空间,有些书本里翻译成了命名空间,对应的英文是namespace。
在C++当中,名称可以是变量、函数、结构体、枚举、类以及结构体和类的成员。这本身并没有问题,但随着项目的增大,名称之间相互冲突的可能性也会大大增加。
比如我们使用了多个厂商的代码,它们都定义了List
,Tree
和Node
类,但定义的方式不同,也就没办法互相兼容。这个时候当我们希望使用一个库的List
类,而使用另外一个的Tree
类,就会非常麻烦。这类冲突被称为名称空间(namespace)问题。
我们先来复习一下几个术语。
声明区域指的是可以在其中进行声明的区域,比如我们可以在函数外侧声明全局变量,对于全局变量,它的声明区域就是其声明所在的文件。对于函数中声明的变量, 它的声明区域就是其声明所在的代码块。
潜在作用域的范围比声明区域更加精确,它从声明语句处开始一直到声明区域的结尾。这是因为变量必须定义之后才能使用,所以潜在作用域的范围比声明区域要小。
这里有一个细节,变量并不一定在整个潜在作用域都是可见的。因为可能还会被嵌套在声明区域中的同名变量隐藏。比如说我们同时定义了一个全局变量和一个函数中的同名变量,那么在函数当中,外侧的全局变量将会被同名的局部变量隐藏。
结合前面所说的,变量对于程序而言可见的范围被称为作用域,它又比潜在作用域更加精确一些。
C++新增了通过定义一种新的声明区域来创建命名的名称空间,这样做的目的是提供一个声明名称的区域。一个名称空间中的名称不会与另外一个名称空间的相同名称发生冲突,同时允许程序的其他部分使用该名称空间中声明的东西。
比如C++ Primer当中的这个例子,下面使用新的关键字namespace
创建了两个名称空间A和B。
namespace A {
double pail;
void fetch();
int pal;
struct Well {...};
}
namespace B {
double bucket(double n) {...}
double fetch;
int pal;
struct Hill {...};
}
名称空间可以是全局的,也可以位于另外一个名称空间中,但不能位于代码块中。因此,默认名称空间里的所有声明的名称的链接性都是外部的,const
关键字修饰的常量除外。
除了用户定义的名称空间之外,还存在另外一个名称空间——全局名称空间。它对应于文件级的声明区域,因此前面所说的全局变量现在被描述为位于全局名称空间中。
任何名称空间中的名称都不会与其他空间的名称发生冲突,因此A中的fetch
可以和B中的fetch
共存。名称空间中的声明和定义规则桶全局声明和定义的规则相同。
名称空间是开放的,可以把名称加入到已经创建的名称空间中,比如:
namespace A {
char *goose(const char *);
}
同样我们之前在名称空间A当中只是定义了函数fetch
,而没有定义,我们也可以在之后的代码当中添加定义:
namespace A {
void fetch () {
...
}
}
当然而我们需要一种方法来访问给定名称空间里的名称,最简单的方法是使用作用域解析符::
,使用名称空间名来找到该名称:
A::pail = 12.34;
A::fetch();
没有作用域解析符的名称成为未限定名称,包含了名称空间的名称称为限定的名称。
这一篇当中涉及了许多概念,看起来有些晦涩。但我个人感觉,这些概念理解起来并不复杂,主要是一些说明性的语言读起来有些难以理解。最好的办法就是沉下气来,一点点精读,先把前面理解了再看后面。