- 命名空间的由来
- 什么是命名空间
- 命名空间的定义
- 同名的命名空间的合并
- 域作用限定符
- 命名空间的三种使用方法
- 头文件与std
- 命名空间的本质
命名空间的由来
我们先来看看下面这段代码,看看能不能编译成功:
//此时文件后缀为.c
#include
int rand = 10;
int main()
{
printf("%d\n", rand);
return 0;
}
这么一看,好像确实没有什么问题?实际上编译器也能成功编译过去。
但如果加上下面的头文件呢?
#include
#include //新加的头文件
int rand = 10;
int main()
{
printf("%d\n", rand);
return 0;
}
结果却是失败的,因为在预处理阶段,stdlib这个头文件会进行展开,而其中就有一个名为rand的函数,展开后该函数同样位于全局区,这就与我们定义的rand变量有名字冲突;
由于这种情况(自己定义的变量名或函数名与其他人的冲突)在大型项目中经常出现,C++就提出了域名空间这么一个概念;
在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称都将存在于全局作用域中,可能会导致很多冲突。适用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或者名字污染,namespace关键字的出现就是针对这种问题的。命名空间相当于一堵围墙,将内部成员与全局域成员隔离开来;
命名空间的定义定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员;
命名空间的成员可以是变量,可以是函数,也可以是结构体:
namespace zjm
{
int rand = 10;
int Add(int x, int y)
{
return x + y;
}
struct Node
{
struct Node* next;
int val;
};
}
命名空间也可以嵌套使用:
namespace zjm
{
int x = 30;
namespace zs
{
int z = 200;
int Sub(int x, int y)
{
return x - y;
}
}
}
既然命名空间可以将内部成员与全局域成员隔离开来,那么利用命名空间我们就可以解决上面代码中的名字冲突问题:
//此时文件后缀为.cpp
#include
#include
namespace zjm
{
int rand = 10;
}
int main()
{
printf("%d\n", zjm::rand);//访问指定命名空间中的成员
printf("%p\n", rand);//访问全局变量
return 0;
}
同名的命名空间的合并
同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间;
例如:test.h 会与 test.cpp中的同名域进行合并
//test.h
namespace zjm
{
int Add(int x, int y)
{
return x + y;
}
}
//test.c
#include"test.h"//必须包含头文件
namespace zjm
{
int x = 30;
namespace zs
{
int z 200;
int Sub(int x, int y)
{
return x - y;
}
}
}
//最终test.h和test.c中zjm命名空间会合并
注意:
在同一个文件中如果出现多个同名域,也是会进行合并的;如果在两个 .cpp文件中出现同名域,在逻辑上也是会合并的,但如果要使用,就必须进行声明;如下:
//test1.cpp
namespace zjm
{
int Add(int x, int y)
{
return x + y;
}
}
//test2.cpp
namespace zjm
{
int Add(int x, int y);//进行函数声明
int Sub(int x, int y)
{
return x - y;
}
}
域作用限定符
在上述代码中,:: 为域作用限定符,其作用是:访问指定域中的成员,如果域中没有该成员则报错; 其使用规则是:左边带命名空间的名字,右边带命名空间中的成员;
注意:
如果域作用限定符左边没有加名字,则表示指定到全局域中去寻找;
int x = 0;
int main()
{
int x = 1;
printf("%d\n", ::x);//指定到全局域中寻找
printf("%d\n", x);//访问局部变量优先
return 0;
}
命名空间的三种使用方法
namespace N
{
int a = 10;
int b = 20;
int Add(int x, int y)
{
return x + y;
}
}
1.加命名空间名称与域限定符
printf("%d\n",N::a);
2.使用using将某个空间中某个成员引入
using N::b;//此时b就会有与全局域成员同名的风险
printf("%d\n",N::a);//由于没有引入a,所以仍然需要加域限定符
printf("%d\n",b);
3.使用using namespace将整个命名空间名称引入
using namespace N;//此时N中的成员就会有与全局域中成员同名的风险
printf("%d\n",a);
printf("%d\n",b);
Add(1,2);
注意:
在使用第三种方法后,相当于破坏了命名空间的所有围墙,将空间内所有成员暴露出来,此时成员就会出现与全局域中的成员同名的风险; 所以该方法,不建议在大型项目中使用,在平时练习时可以使用;第二种方法会破坏你所指定的成员的围墙,所指定的成员就会有与全局域成员同名的风险;
我们在写C++时,首先打下的代码常常会是下面这两句:
#include
using namespace std;
早期标准库将所有功能全部放在全局域中实现,其头文件也是以 .h 结尾;在后来为了将C++的库与C的区分开来,也为了防止命名冲突,C++将库的功能实现全部用命名空间包装起来,这个命名空间便是std,也规定了C++的头文件都不带.h结尾(早期编译器还是支持.h结尾的);
命名空间的本质在这里先提出一个问题:在命名空间中的成员是局部变量,还是全局变量?
这里需要先搞明白一个点:局部变量是存在栈区的,什么情况下才会建立栈帧呢?只有在函数调用时才会建立栈帧;也就是说局部变量只会存在函数中;
那么命名空间是函数吗?很明显不是!命名空间本质只是将空间域中的成员与全局域中的数据隔离开来,使其成员不会与全局域中的成员重名,其成员本质仍是全局变量,只是访问时需要使用域作用限定符;
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)