前言

想必大家在初学C++时就已经发现了这样一个现象:一些代码前面要加上 std:: 或者在头文件后用 using namespce std;来代替。已经学习C++两年的我在最开始发现这一问题时选择了最简单的方法来理解这一现象,那就是“记住就好了,就应该是这样的”。

直到最近在阅读《程序员的自我修养》这本书的时候又一次看到了 namespace 这一词,这次它以一个全新的名字出现在我的眼前——命名空间。在查询了资料过后,才解开了这个困惑。

什么是命名空间

命名空间(Namespace)是编程语言中用于标识符(变量、函数、类、对象等)命名的一种机制,它可以防止命名冲突和提供更好的代码组织方式。在不同的上下文中,同一个名字可能会被用来表示不同的实体,这时候通过命名空间可以区分它们,避免混淆。

我们假设一个C++文件是一个城市,在这个城市里有很多人都叫“张伟”,如果我们除了他们的名字以外对他们没有任何了解,那么当我们叫张伟这个名字时,编译器并不知道是哪一个张伟。更何况我们叫的这个名字,在别的城市(引入的其他头文件)也有。

为了更好分清楚他们,我们将收集更多关于这些人的个人信息,如家庭住址、年龄、身高、长相等。

由此引入命名空间这个概念,我们给每一个这样的人限定一定的空间范围,在每次使用时到提前声明他的空间范围是什么,这样编译器就可以分清楚我们要的是哪一个。本质上,命名空间就是定义了一个范围。

如何声明并使用命名空间

具体示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 定义命名空间
namespace MyNamespace {
// 声明或定义变量、函数、类等实体
int myVariable = 42;

void myFunction() {
// 函数实现
}

class MyClass {
// 类定义
};
}

// 使用命名空间中的实体
int main() {
MyNamespace::myFunction();
int value = MyNamespace::myVariable;
MyNamespace::MyClass obj;
return 0;
}

using namespace std;

相信看到这里,一部分人就已经明白这一条代码是什么意思了。这是一个使用命名空间的声明,它的作用是告诉编译器在当前代码范围内使用标准 C++ 库(std)的所有实体,而无需在每个标识符前加上 std:: 前缀。

在 C++ 中,std 是标准 C++ 库(Standard Library)的命名空间,它包含了许多有用的函数、类和对象,用于完成各种常见的任务,如输入输出、容器操作、算法处理、字符串处理等等。标准 C++ 库是 C++ 语言的一部分,提供了广泛的功能,使得开发者可以更加高效地编写 C++ 程序。

std 是 C++ 标准库的命名空间,因此库中的所有实体(函数、类、对象等)都被放在这个命名空间下,以避免与用户代码中的其他实体发生命名冲突。

下面分别展示使用命名空间以及不使用命名空间的写法:

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>

using namespace std; // 使用了std命名空间

int main() {
string name;
cout << "请输入你的名字:";
cin >> name;
cout << "你好," << name << "!" << endl;

return 0;
}
1
2
3
4
5
6
7
8
9
10
#include <iostream>

int main() {
std::string name;
std::cout << "请输入你的名字:";
std::cin >> name;
std::cout << "你好," << name << "!" << std::endl;

return 0;
}

显然,在代码教程的情况下,使用 using namespace std; 可以少写很多代码。

using指令

您可以使用 using namespace 指令,这样在使用命名空间时就可以不用在前面加上命名空间的名称。这个指令会告诉编译器,后续的代码将使用指定的命名空间中的名称。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
using namespace std;

// 第一个命名空间
namespace first_space{
void func(){
cout << "Inside first_space" << endl;
}
}
// 第二个命名空间
namespace second_space{
void func(){
cout << "Inside second_space" << endl;
}
}
using namespace first_space;
int main ()
{

// 调用第一个命名空间中的函数
func();

return 0;
}

以上代码的输出结果为:

image-20230731204222913

using 指令引入的名称遵循正常的范围规则。名称从使用 using 指令开始是可见的,直到该范围结束。此时,在范围以外定义的同名实体是隐藏的。

不连续的命名空间

命名空间可以定义在几个不同的部分中,因此命名空间是由几个单独定义的部分组成的。一个命名空间的各个组成部分可以分散在多个文件中。

所以,如果命名空间中的某个组成部分需要请求定义在另一个文件中的名称,则仍然需要声明该名称。下面的命名空间定义可以是定义一个新的命名空间,也可以是为已有的命名空间增加新的元素:

1
2
3
namespace namespace_name {
// 代码声明
}

嵌套的命名空间

命名空间可以嵌套,您可以在一个命名空间中定义另一个命名空间,如下所示:

1
2
3
4
5
namespace namespace_name1 {   // 代码声明   
namespace namespace_name2 {
// 代码声明
}
}

您可以通过使用 :: 运算符来访问嵌套的命名空间中的成员:

1
2
3
4
5
// 访问 namespace_name2 中的成员 
using namespace namespace_name1::namespace_name2;

// 访问 namespace_name1 中的成员
using namespace namespace_name1;

在上面的语句中,如果使用的是 namespace_name1,那么在该范围内 namespace_name2 中的元素也是可用的,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
using namespace std;

// 第一个命名空间
namespace first_space{
void func(){
cout << "Inside first_space" << endl;
}
// 第二个命名空间
namespace second_space{
void func(){
cout << "Inside second_space" << endl;
}
}
}
using namespace first_space::second_space;
int main ()
{

// 调用第二个命名空间中的函数
func();

return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

1
Inside second_space

结束语

以上便是笔者解决自己疑惑所学到的内容,其中主要内容来自于菜鸟教程和ChatGPT的回答。本文仅作为笔者自己地思考笔记,希望对你有用。