在C语言中有下面一段代码:
#include <stdio.h>
int main() {
int scanf = 0;
scanf("%d", &scanf);
return 0;
}
这段代码会报错。
首先 scanf 是一个函数名,不是C语言的关键字,所以定义一个名为 scanf 的局部变量是没有任何问题的。
但是,当我想调用 scanf 函数去给定义的临时变量 scanf 赋值的时候却出现了问题。
我们以为是函数名的 scanf 也被编译器当成了临时变量:
亦或者是下面一段代码:
#include <stdio.h>
int scanf = 0;
int main() {
printf("%d", scanf);
return 0;
}
这段代码报错说 scanf 重定义。这段代码与上面不同的是 scanf 定义成了全局变量,而在头文件 stdio.h 中 scanf 也被定义了一次。在预处理阶段会进行头文件的展开,这样就导致了报错所说的那个问题。
上面两个案例就引出了一个问题——命名污染。
C++ 为了解决这个问题,引入了命名空间。
命名空间的定义
定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。
比如下面的一些使用方法:
//1. 普通的命名空间
namespace N1 // N1为命名空间的名称
{
// 命名空间中的内容,既可以定义变量,也可以定义函数
int a;
int Add(int left, int right)
{
return left + right;
}
}
//2. 命名空间可以嵌套
namespace N2 {
int a;
int b;
int Add(int left, int right) {
return left + right;
}
namespace N3 {
int c;
int d;
int Sub(int left, int right){
return left - right;
}
}
}
//3. 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。
namespace N1 {
int Mul(int left, int right) {
return left * right;
}
}
解释一下第三段代码。
它和第一段代码的命名空间都叫 N1 ,命名空间相同的命名符号会在链接过程中合并,所以 a、Add、Mul 是同属一个命名空间的。
一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中,当出了这个命名空间的作用域,里面定义的各种变量名也就跳出了生命周期。
命名空间的使用
我们在初次接触C++的代码时可能会看到下面的代码:
//1
using namespace std;
int main() {
cout << "hello world" << endl;
return 0;
}
//2
int main() {
std::cout << "hello world" << std::endl;
return 0;
}
//3
using std::cout;
int main() {
cout << "hello world" << endl;
return 0;
}
其实上面三段代码就反映了命名空间的三种主要使用方法。
下面解释一下。
首先说明一点,cout 是*C++*的输出函数,它是定义在命名空间 std 中的,std 中还定义了一个输入函数 cin ,下面讲解会拿它来举例。
1 处是将命名空间 std 全部展开,这也就意味着 std 中的任何一个变量名都不能被再次重定义,假如我添加一行 int cin = 0;
编译器就会报错。这样的好处是对命名空间中的变量名可以直接使用,而不用加限定符。但缺点也同样明显,就是在协同工作中可能会引发命名冲突的问题
2 处是使用了命名空间名称+作用域限定符的方式。这样的好处是避免了命名冲突的问题,即使我又定义了一个 int cout = 0;
编译器也不会报错。缺点同样明显,就是使用起来太麻烦了。
3 处是工程中最标准也是最推荐的使用方式,对命名空间中的哪个成员用的多就单独把它引用进来,这样只需要避免重定义引进来的这个成员就可以规避命名冲突,这样最大程度地避免了命名冲突的问题,写起来代码也不会太麻烦。