C++的命名空间namespace详解及特殊情况分析

历史来源

最开始的C++ 头文件仍然以.h为后缀,它们所包含的类、函数、宏等都是全局范围的。后来 C++ 引入了命名空间,计划重新编写库将类、函数、宏等都统一纳入一个命名空间std
但改版后的c++库致使旧c++库无法使用,在当时产生了巨大的反响,于是,C++ 开发人员想了一个好办法,保留原来的库和头文件,它们在 C++ 中可以继续使用,然后再把原来的库复制一份,把类、函数、宏等纳入命名空间 std 下,就成了新版 C++ 标准库。这样共存在了两份功能相似的库,使用了老式 C++ 的程序可以继续使用原来的库,新开发的可以使用新版的 C++ 库。
为了避免头文件重名,新版 C++ 库也对头文件的命名做了调整,去掉了后缀.h而对于原来C语言的头文件,也采用同样的方法,但在每个名字前还要添加一个c字母,stdio.h变成了cstdio,stdlib.h变成了cstdlib

所以,命名空间(namespace)std是C++标准库(Standard Library)中定义的,其中一系列标准库提供的类、函数和对象都在c++标准库中,准确来说在标准库的std当中。

意义

提到命名空间就不得不提到C语言了,由于C++是C语言的扩展,所以C++的开发人员就注意到C语言中命名冲突,例如一个团队的多个人员参与了一个文件管理系统的开发,他们都定义了一个全局变量 a,用来指明当前打开的文件,将他们的代码整合在一起编译时,很明显编译器会提示 重复定义(Redefinition)错误。

有一个常用的经典案例

#include<stdio.h>
#include<stdlib.h>
int rand = 0;//全局变量
int main()
{
	printf("%d\n", rand);
	return 0;
}       
// 出现了编译错误,error C2365: “rand”: 重定义;以前的定义是“函数”
//因为rand是一个C语言标注库中的函数,在编译时发现你使用的库中函数一样的名字就报错了

这个案例我们会在文章末尾进行再度分析

结论😗: 没有命名空间的话,在C/C++中,标识符(变量、函数和类)都是大量存在的,并且都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染.

定义

为了解决合作开发时的命名冲突问题,C++ 引入了命名空间Namespace的概念。
当你要定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{ }即可,{ }中即为命名空间的成员

①普通定义:

//项目中成员分别以自己的名字定义了命名空间
namespace zs//张三的变量定义
{  
    int a = 1;
}
namespace ls //李四的变量定义
{  
    int a = 2;
}

②嵌套定义


namespace demo1
{
	int a = 0;
	int Add(int left, int right)
	{
		return left + right;
	}
	namespace demo2
	{
		int b = 0;
	}
}

③不同文件同名命名空间合并

// test.cpp
namespace N1
{
	int a;
	int b;
	int Add(int left, int right)
	{
		return left + right;
	}
}

// test.h
namespace N1
{
	int Mul(int left, int right)
	{
		return left * right;
	}
}
//test.cpp 和 test.h中的 N1 最终会合并为一个

使用

正因为有了命名空间,所以使用对于的变量函数等,就要指明是具体哪个命名空间
在C++ 中 using 用于声明命名空间至全局命名空间(说白了就是解开std的束缚,让命名空间std完全暴露出来),使用命名空间也可以防止命名冲突。
:: 是一个新符号,称为域解析操作符,在C++中用来指明要使用的命名空间

1.命名空间名称+域解析操作符

//对应上面第一个普通定义的例子
zs::a = 10;
ls::a = 20;

2.using + 命名空间 :: 一个成员

using zs::a; //它的意思是,using 声明以后的程序中如果出现了未指明命名空间的 a
			 //就使用 zs::a;但是若要使用ls定义的 a,仍然需要 ls::a。

a = 10; //此时的a时zs命名空间
ls::a = 20;//ls命名空间

3.using namespace +命名空间

using namespace zs; //在 using 声明后,如果有未具体指定命名空间变量产生了命名冲突
			        //那么默认采用命名空间 zs 中的变量。
a = 10; //此时的a时zs命名空间
ls::a = 20;//ls命名空间

对于上面的那个经典案例,我们就可以用到命名空间的知识进行解决

#include<iostream>
namespace hk
{
	int rand = 0;
};
int main()
{
	printf("%d", hk::rand); //用到第一种方法进行解决
	return 0; 
}

但是如果你使用第二种,第三种方法,就会出错
在这里插入图片描述
在这里插入图片描述

接下来我们就着重对这个例子进行分析
首先可能会有人有疑惑为什么 头文件是< iostream > ,并没有< cstdio >为什么也会找到< cstdio >里面的rand()函数

那是因为头文件的引用时进行了层层包含
iostraeam 里面引用了
#include < istream >
istream
引用了
#include < ostream >
引用了
#include < ios >
引用了
#include < xlocnum >
引用了
#include < cstdlib >
里面有如下定义
在这里插入图片描述

通过上图就可以发现,在使用C++的模式下使用C语言库里面的,其实头文件的里面就使用using打开了命名空间的束缚,所以就出现了上面经典案例中用第二种和第三种方法时,即使你根本没有手动使用using去声明这个命名空间(即命名空间暴露出来),但是仍然能够使用std里面的rand()函数.因为头文件里面自己就打开了!!!

并且按照 C++ 的方式来使用C语言的头文件,即#include < cstdio >这种形式,那么符号可以位于命名空间 std 中,也可以位于全局范围中.
①.使用命名空间std

#include <cstdio>
int main()
{
    std::printf("kklovecode");
    return 0;
}

②.不使用命名空间 std

#include <cstdio>
int main()
{
    printf("kklovecode");
    return 0;
}   //在大部分编译器中都能通过

你也能在头文件中找到使用using打开了std的束缚
在这里插入图片描述

using namespace std弊端

相信大家在看许多C++资料时,都会发现很多都是直接在头文件下面写上 using namespace std;用的就是第三种方法使用std命名空间
如:

#include<iostraeam>
using namespace std;
int main()
{
  ...
}

因为标准库可能会升级,这样升级编译使用的C++版本的时候有可能因为引入了新的符号跟自己代码里的命名冲突,这样做增加了命名冲突的风险.
并且C++设置std命名空间就是不想你直接使用,而当你直接用using namespace std; 将其暴露出来,也多少有点违背C++本意.
总结😗:但一般来说,升级C++版本最多几年也就做一次,冲突的可能性也并不大,所以很多教材中的代码像那样使用节约时间成本也并非没有道理,但在中大型项目开发中是不被推荐的,适合使用第二种或者第三种方式

  • 17
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kklovecode

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值