目录
1. C语言的命名冲突问题
在C 中,函数名、变量名、类名都大量存在于全局域中,这使得命名冲突的情况时有发生:
#include<stdio.h>
#include<stdlib.h>
int rand = 10;
int main() {
printf("%p\n", rand);
return 0;
}
编译器会报错:
当运行时会默认首先在局部域寻找变量rand,寻找失败时会到全局域中寻找,全局域中有两个rand,一个是标准库stdlib.h中的rand函数,一个是定义的全局变量rand=0;
此时会发生命名冲突,编译器报错。
2. C++的命名空间与域
1、namespace本质是定义一个域,这个域与全局域各自独立,不同的域可以定义同名变量,从而解决命名冲突问题;
3、C++中的域包括有函数局部域,全局域,命名空间域,类域:
域影响的是编译时语法查找一个变量/函数/类型出处(声明或定义)的逻辑;
(1)局部域和全局域不仅会影响编译查找逻辑,还会影响变量生命周期;
(2)命名空间域和类域不影响变量声明周期;
4、使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对该问题。
5、命名空间只能在全局域中定义,不能在某函数局部域内定义命名空间,但可以在命名空间中嵌套定义;
6、程序编译时对变量作用域查找顺序:现默认在函数局部域内查找,没有找到再到全局域查找;
7、同一个命名空间可以在同个文件或多个文件重复定义,程序编译时会将相同名称的命名空间进行合并;
3. 命名空间的使用
在定义命名空间后,在实际编程中可以通过两种方式进行使用:
方式1:定义或使用变量时指定命名空间:;
方式2:展开命名空间
3.1 指定命名空间
定义命名空间,需要使用到namespace关键字,使用格式为namespace 命名空间名 { },
其中{ }中即为命名空间的成员,命名空间中可以定义变量/函数/类型等;
示例1:简单变量
C++兼容C,仍然采用C语言的输出方式。
#include<stdio.h>
#include<stdlib.h>
namespace demo {
int rand = 10;
}
int main() {
// 打印全局域中stdlib.h中rand库函数的地址
printf("%p\n", rand);
// 打印demo命名空间中变量rand值
printf("%p\n", demo::rand);
return 0;
}
运行结果如下:
设置demo命名空间,使用域作用限定符 :: 指定变量为demo命名空间的变量而非标准库中的函数地址;
注:对于全局域,也可不在域作用限定符前增加命名空间名来表示(即以下两种写法等价):
printf("%p\n", rand);
printf("%p\n", ::rand);
示例2:函数与结构体
#include<stdio.h>
#include<stdlib.h>
namespace demo1
{
// 命名空间中可以定义变量/函数/类型
int rand = 10;
int Add(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
}
int main() {
printf("%d\n", demo1::Add(8, 5));
struct demo1::Node node1;
node1.val = 1997;
printf("%d\n", node1.val);
return 0;
}
输出结果如下:
注:定义未重命名的结构体变量时,注意指定命名空间的位置,对于上例的strcut Node而言:
正确写法为:struct demo1::Node node1;
错误写法为:demo1::struct Node node1;
示例3:命名空间嵌套
#include<stdio.h>
#include<stdlib.h>
namespace Demo2 {
namespace demo21 {
int rand = 1;
int Add(int left, int right) {
return left + right;
}
}
namespace demo22 {
int rand = 2;
int Add(int left, int right) {
return left + right;
}
}
}
int main() {
printf("%d\n", Demo2::demo21::rand);
printf("%d\n", Demo2::demo22::rand);
return 0;
}
运行结果如下:
3.1 命名空间的展开
1、每次都指定命名空间在实际中非常麻烦,使用时还可以将命名空间展开:
#include<stdio.h>
#include<stdlib.h>
namespace demo3 {
int a = 85;
int b = 1997;
}
using namespace demo3;
int main() {
printf("a = %d\n", a);
}
2、(1)对于指定命名空间方式:在项目中更推荐使用指定命名空间的使用方式;
(2)对于展开命名空间方式:
① 展开命名空间中所有成员的使用方式在项目中不推荐使用,冲突风险很大;
② 项目中经常访问的不存在冲突的成员更推荐使用展开命名空间的使用方式。
3、C++标准库都放在一个叫std(standard)的命名空间中,包括cin、cout对象和enl函数等等,在日常练习中可以使用using namespace std; 展开标准库,但在项目中会增加冲突概率:
(1)指定std命名空间:
#include<iostream>
int main() {
int i = 9785;
int j = -9785;
std::cout << i << std::endl;
return 0;
}
(2)展开std命名空间:
#include<iostream>
using namespace std;
int main() {
int i = 9785;
int j = -9785;
cout << i << endl;
return 0;
}
4、当一个命名空间中部分成员使用频繁,而另外一部分使用很少时,可以采用展开命名空间中部分成员的方法:
#include<stdio.h>
#include<stdlib.h>
namespace demo3 {
int a = 85;
int b = 1997;
}
using demo3::a;
int main() {
printf("a = %d\n", a);
printf("a = %d\n", a);
printf("a = %d\n", a);
printf("a = %d\n", a);
printf("a = %d\n", a);
printf("b = %d\n", demo3::b);
}
5、头文件的展开和命名空间的展开:
(1)程序编译只会编译.c/.cpp文件,不会编译.h文件。头文件的展开是指编译器将.h文件拷贝到.c/.cpp文件中;
(2)命名空间的展开是指编译器在查找变量/函数/类型出处(声明或定义)时会在该展开的命名空间内去查找;