C++ —— 命名空间

C++ —— 命名空间,第1张

文章目录
  • 命名空间的由来
  • 什么是命名空间
  • 命名空间的定义
  • 同名的命名空间的合并
  • 域作用限定符
  • 命名空间的三种使用方法
  • 头文件与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);

注意:
在使用第三种方法后,相当于破坏了命名空间的所有围墙,将空间内所有成员暴露出来,此时成员就会出现与全局域中的成员同名的风险; 所以该方法,不建议在大型项目中使用,在平时练习时可以使用;第二种方法会破坏你所指定的成员的围墙,所指定的成员就会有与全局域成员同名的风险;

头文件与std

我们在写C++时,首先打下的代码常常会是下面这两句:

#include
using namespace std;

早期标准库将所有功能全部放在全局域中实现,其头文件也是以 .h 结尾;在后来为了将C++的库与C的区分开来,也为了防止命名冲突,C++将库的功能实现全部用命名空间包装起来,这个命名空间便是std,也规定了C++的头文件都不带.h结尾(早期编译器还是支持.h结尾的);

命名空间的本质

在这里先提出一个问题:在命名空间中的成员是局部变量,还是全局变量?
这里需要先搞明白一个点:局部变量是存在栈区的,什么情况下才会建立栈帧呢?只有在函数调用时才会建立栈帧;也就是说局部变量只会存在函数中;
那么命名空间是函数吗?很明显不是!命名空间本质只是将空间域中的成员与全局域中的数据隔离开来,使其成员不会与全局域中的成员重名,其成员本质仍是全局变量,只是访问时需要使用域作用限定符;

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/2991608.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-09-23
下一篇 2022-09-23

发表评论

登录后才能评论

评论列表(0条)

保存