C++ 编程
命名空间关键字允许您创建一个新的作用域。名称是可选的,可以省略以创建未命名命名空间。创建命名空间
后,您必须显式引用它或使用using
关键字。命名空间由namespace
块定义。
- 语法
namespace name {
declaration-list;
}
在许多编程语言中,命名空间是标识符的上下文。C++ 可以处理语言中的多个命名空间。通过使用namespace
(或using namespace
关键字),可以提供一种干净的方式将代码聚合到一个共享的标签下,以防止命名冲突或仅仅为了方便回忆和使用非常特定的作用域。除了“命名空间”之外,还有其他“名称空间”;这可能会令人困惑。
名称空间(注意那里的空格),正如我们将看到的,通过提供一种简单的方法来区分正在调用/使用什么,超越了作用域的概念。正如我们将看到的,类也是名称空间,但它们不是命名空间。
- 示例
namespace foo {
int bar;
}
在此块内,标识符可以按其声明的方式使用。在此块之外,必须添加namespace
说明符(即,它必须是限定的)。例如,在namespace foo
之外,bar
必须写成foo::bar
。
C++ 包含另一个构造,它使这种冗长性变得不必要。通过将行using namespace foo;
添加到代码段中,不再需要前缀foo::
。
没有名称的namespace
称为未命名命名空间。对于这样的namespace
,将为每个翻译单元生成一个唯一的名称。无法将using
关键字应用于未命名命名空间,因此未命名命名空间的工作方式就好像已将using
关键字应用于它一样。
- 语法
namespace {
declaration-list;
}
您可以为命名空间创建新名称(别名),包括嵌套命名空间。
- 语法
namespace identifier = namespace-specifier;
- 使用
using namespace std;
这使用-指令指示程序中使用的但未声明的任何名称都应在“标准(std)”命名空间
中查找。
要使来自命名空间
的单个名称可用,以下使用-声明存在
using foo::bar;
在此声明之后,名称bar可以在当前命名空间
内使用,而不是更冗长的版本foo::bar。请注意,程序员经常互换使用术语“声明”和“指令”,尽管它们在技术上的含义不同。
最佳实践是使用狭义的第二种形式(使用声明),因为广义的第一种形式(使用指令)可能会使比所需更多的名称可用。示例
namespace foo {
int bar;
double pi;
}
using namespace foo;
int* pi;
pi = &bar; // ambiguity: pi or foo::pi?
在这种情况下,声明using foo::bar;只会使foo::bar可用,避免了pi和foo::pi的冲突。此问题(相同名称的变量或函数的冲突)称为“命名空间污染”,原则上应尽可能避免。
使用-声明可以出现在许多不同的位置。其中包括
- 命名空间(包括默认命名空间)
- 函数
一个使用-声明使名称(或命名空间
)在声明的作用域内可用。示例
namespace foo {
namespace bar {
double pi;
}
using bar::pi;
// bar::pi can be abbreviated as pi
}
// here, pi is no longer an abbreviation. Instead, foo::bar::pi must be used.
命名空间是分层的。在假设的命名空间
food::fruit中,标识符orange引用food::fruit::orange(如果存在),否则引用food::orange(如果存在)。如果两者都不存在,orange则引用默认命名空间
中的标识符。
未在命名空间
中显式声明的代码被认为在默认命名空间中。
命名空间的另一个属性是它们是开放的。一旦声明了命名空间
,就可以重新声明(重新打开)它,并且可以添加命名空间
成员。示例
namespace foo {
int bar;
}
// ...
namespace foo {
double pi;
}
命名空间最常用于避免命名冲突。尽管在最近的 C++ 代码中广泛使用了命名空间,但大多数旧代码未使用此功能。例如,整个标准库是在命名空间
std中定义的,在该语言的早期标准中,在默认命名空间中。
对于较长的命名空间名称,可以定义较短的别名(命名空间
别名声明)。示例
namespace ultra_cool_library_for_image_processing_version_1_0 {
int foo;
}
namespace improc1 = ultra_cool_library_for_image_processing_version_1_0;
// from here, the above foo can be accessed as improc1::foo
存在一个特殊的命名空间
:未命名命名空间。此命名空间
用于特定源文件或其他命名空间
私有的名称
namespace {
int some_private_variable;
}
// can use some_private_variable here
在周围的作用域中,可以访问未命名命名空间的成员,而无需限定,即无需使用命名空间名称和::(因为命名空间没有名称)。如果周围的作用域是命名空间
,则可以将成员视为并访问为其成员。但是,如果周围的作用域是文件,则无法从任何其他源文件访问成员,因为无法将文件命名为作用域。未命名命名空间声明在语义上等效于以下构造
namespace $$$ {
// ...
}
using namespace $$$;
其中$$$是由编译器生成的唯一标识符。
您可以将未命名命名空间嵌套在普通命名空间
中,反之亦然,您也可以嵌套两个未命名的命名空间。
namespace {
namespace {
// ok
}
}
由于空间限制,我们实际上无法显示命名空间
命令的正确使用方法:它需要一个非常大的程序才能显示其有效工作。但是,我们可以轻松地说明其概念本身。
// Namespaces Program, an example to illustrate the use of namespaces
#include <iostream>
namespace first {
int first1;
int x;
}
namespace second {
int second1;
int x;
}
namespace first {
int first2;
}
int main(){
//first1 = 1;
first::first1 = 1;
using namespace first;
first1 = 1;
x = 1;
second::x = 1;
using namespace second;
//x = 1;
first::x = 1;
second::x = 1;
first2 = 1;
//cout << 'X';
std::cout << 'X';
using namespace std;
cout << 'X';
return 0;
}
我们将从程序的开头向下移动到程序的末尾检查代码,依次检查代码片段。
#include <iostream>
这仅包含iostream库,以便我们可以使用std::cout将内容打印到屏幕上。
namespace first {
int first1;
int x;
}
namespace second {
int second1;
int x;
}
namespace first {
int first2;
}
我们创建一个名为first的命名空间,并在其中添加两个变量first1和x。然后我们关闭它。然后我们创建一个名为second的新命名空间,并在其中放入两个变量:second1和x。然后我们重新打开命名空间
first,并在其中添加另一个名为first2的变量。可以像这样多次重新打开命名空间
,以添加额外的名称。
main(){
1 //first1 = 1;
2 first::first1 = 1;
主程序的第一行被注释掉了,因为它会导致错误。为了访问第一个命名空间
中的名称,我们必须在变量名称前加上其命名空间
的名称和两个冒号;因此,主程序的第二行不是语法错误。变量的名称在作用域内:它只需要以这种特殊的方式被引用,然后才能在此处使用。因此,这将全局名称列表分割成组,每组都有自己的前缀名称。
3 using namespace first;
4 first1 = 1;
5 x = 1;
6 second::x = 1;
主程序的第三行引入了using namespace命令。此命令将第一个命名空间
中的所有名称拉入作用域。然后可以从那里以通常的方式使用它们。因此,程序的第四行和第五行可以编译而不会出错。特别是,变量x现在可用:为了访问第二个命名空间
中的另一个变量x,我们将它称为second::x,如第六行所示。因此,这两个名为x的变量可以分别引用,就像它们在第五行和第六行一样。
7 using namespace second;
8 //x = 1;
9 first::x = 1;
10 second::x = 1;
然后我们再次使用using namespace命令将命名空间
名为second中的声明拉入。下一行被注释掉了,因为它现在是一个错误(而之前是正确的)。由于这两个命名空间都已被引入全局名称列表,因此变量x现在变得不明确,并且需要仅以第九行和第十行所示的限定方式进行讨论。
11 first2 = 1;
主程序的第十一行表明,即使first2是在命名空间
名为first的单独部分中声明的,它与命名空间
first中的其他变量具有相同的状态。可以根据需要多次重新打开命名空间
。当然,作用域的通常规则适用:在同一个命名空间
中两次声明相同的名称是非法的。
12 //cout << 'X';
13 std::cout << 'X';
14 using namespace std;
15 cout << 'X';
}
计算机中有一组特殊的文件中定义了一个命名空间
。它的名称是std,所有系统提供的名称,例如cout,都在该命名空间
中的一系列不同文件中声明:这是一个非常大的命名空间。请注意,程序最顶部的#include语句并没有完全引入命名空间
:名称在那里,但仍然必须以限定形式引用。第十二行必须被注释掉,因为目前系统提供的名称(如cout)不可用,除非以限定形式std::cout,如第十三行所示。因此,我们需要像第十四行这样的行:写入该行后,所有系统提供的名称都可用,如程序的最后一行所示。此时,我们已将三个命名空间
的名称合并到程序中。
如示例程序所示,根据需要引入所需的声明,并排除不需要的声明,并且可以使用带双冒号的限定形式以受控的方式引入它们。这为大型程序提供了所需的更好的名称控制。在上面的示例中,我们只使用了变量的名称。但是,命名空间也同样根据需要控制过程和类的名称。