C++ -命名空间-详解

夜泉_ly 2024-10-19 09:35:01 阅读 73

博客主页:【夜泉_ly】

本文专栏:【C++】

欢迎点赞👍收藏⭐关注❤️

C++ -命名空间-详解

1.C语言缺点之一 -- 命名冲突2.命名空间2.1定义2.2使用访问命名空间中的变量展开命名空间域指定访问命名空间域

2.3其他功能

3.C++ 标准库中的命名空间指定展开

4.总结

1.C语言缺点之一 – 命名冲突

先来看看下面这段简单的C语言代码:

<code>#include <stdio.h>

int rand = 0;

int main()

{

printf("%d\n", rand);

return 0;

}

运行结果如下:

0

毫无疑问的结果对吧,那如果我再引入这样一个头文件呢:

#include <stdlib.h>

运行结果如下图:

在这里插入图片描述

报错了 ! 原因呢?

在这里插入图片描述

命名冲突 ——如果学过C语言,那应该能知道在<code><stdlib.h>里面有一个函数,用于生成随机数,而这个函数的名字就是rand,此处,我也定义了同名的变量rand,这就导致了命名冲突。

在C语言中,全局域中命名冲突非常常见,主要有以下两种情况:

1.与库函数冲突:

这就是上面代码报错的原因。

在C语言项目中,可能某天引入了一个库,结果出现了一大堆冲突。

2.开发者相互之间冲突:

在大型项目中,往往都是项目组,这代表不是一个人在写。

那么有没有一种可能,张三写的和李四写的冲突了,但在他们电脑上都能跑。有一天,不同模块合并到一起,结果发现冲突了,这时必然会有一个人要改他的代码。

如果张三、李四都将这个名字在他们的代码里用了几百上千词,那么要修改将是一个极其痛苦的事情。

而C++的祖师爷,下图的本贾尼·斯特劳斯特卢普,同样受够了C语言中的这个缺点,于是在C++中,创造了一个关键字namespace ,用来解决全局域中命名冲突的问题。

在这里插入图片描述

2.命名空间

2.1定义

命名空间可以定义出一个,将其中的内存隔离在这个独立区域,不会与其他区域中的名字冲突:

<code>namespace ly

{

int rand = 0;

}

在上面的代码中,我定义了一个名为ly命名空间,里面包含了变量rand。通过这种方式,避免了与标准库rand函数的冲突。

此处,ly是命名空间的名字,这里我取的是我博客名字【夜泉_ly】中的一部分,而一般开发中是用项目名字做命名空间名。

2.2使用

访问命名空间中的变量

在C语言的学习中,可以了解到,同一个域不能定义同名变量,而不同的域可以定义同名变量。

例如,全局作用域和局部作用域:

#include <stdio.h>

int a = 0;//全局域--全局变量

int main()

{

int a = 1;//局部域--局部变量

printf("%d\n", a);

return 0;

}

C语言的域会影响生命周期,也会影响访问,比如上面这段代码的运行结果是1,因为优先访问的是局部变量。

那么有人问了,如果我就想访问全局变量呢?

也是有方法的:

#include <stdio.h>

int a = 0;//全局域--全局变量

int main()

{

int a = 1;//局部域--局部变量

printf("%d\n", ::a);

return 0;

}

这里使用了域作用限定符::,其作用是对左边的域进行访问,此处::左边没有域,即左边为空白,代表对全局域进行访问。

再看下面这种情况:

#include <stdio.h>

int a = 0;//全局域--全局变量

namespace ly

{

int a = 2;//命名空间域

}

int main()

{

int a = 1;//局部域--局部变量

printf("%d\n", a);

return 0;

}

这里出现了三种域,局部域、全局域、命名空间域,按什么顺序访问?

第一次打印,结果为1,因为默认局部域优先。

再注释掉局部变量a:

//int a = 1;//局部域--局部变量

第二次打印,结果为0,因为局部域未找到指定变量时,会去全局域找。

再注释掉全局变量a:

//int a = 0;//全局域--全局变量

第三。。没有第三次打印了:

在这里插入图片描述

又报错了 ! 原因呢?

在这里插入图片描述

未声明a ——这是因为编译器并不会主动去命名空间域中搜索,所以会报错。

如何访问命名空间域呢?

一般而言,有两种方法:展开命名空间域 OR 指定访问命名空间域。

展开命名空间域

使用<code>using关键字:

using namespace ly;

在一些C++程序开头,常常看见下面这两句代码:

#include <iostream>

using namespace std;

现在可以知道第二句在干什么了:展开了C++标准库的命名空间域。

指定访问命名空间域

printf("%d\n",ly::a);

即在域作用限定符::前加上要访问的命名空间。


现在使用第一个方法,展开命名空间域,来看看下面这段代码会输出什么值?😄

#include <stdio.h>

int a = 0;//全局域--全局变量

namespace ly

{

int a = 2;//命名空间域

}

using namespace ly;//展开命名空间域

int main()

{

printf("%d\n", a);

return 0;

}

什么都输出不了!又报错了:

在这里插入图片描述

其实此处的两个a又冲突了:

展开命名空间域,是将命名空间域中的内容全部暴露到全局域,而同一个域不能定义同名变量,所以编译器会报错。

因此,不要轻易使用<code>using namespace!


回到开头,如果想放心的使用rand作为变量名,可以使用命名空间,并通过指定访问命名空间域的方式访问:

#include <stdio.h>

#include <stdlib.h>

namespace ly

{

int rand = 0;

}

int main()

{

printf("%d\n", ly::rand);//打印rand变量的值

printf("%p\n", rand);//打印rand函数的地址

return 0;

}

此时可以成功输出结果,如图:

在这里插入图片描述

或许会有人问:为什么不能定义一个局部变量来解决问题?

需注意的是,此处的问题就是定义了一个全局变量,而这个全局变量与库冲突了。因此不能通过定义局部变量解决问题。


2.3其他功能

除了变量,命名空间还可以包含其他内容,例如函数、结构体等。

命名空间甚至可以嵌套使用:

<code>namespace N1

{

int a = 1;

namespace N2

{

int a = 2;

}

}

访问时也需多次使用::

printf("%d\n",N1::N2::a);//输出2

通过嵌套命名空间,开发者可以进一步组织和管理代码。


3.C++ 标准库中的命名空间

在学习C语言时,有时也会看到一些C++的代码,这时或许会产生疑问,为什么C++无.h,比如:

#include <iostream.h>

其实,在很早之前,上面这句代码是存在的,在一些老编译器(如VC6.0)上也可以运行,但那时没有命名空间,也不需要展开。

而后来为了与C语言进行区分,并且有了命名空间,C++库、STL等被封进std,这时.h就被去掉了。

现在,标准库通过std命名空间进行管理,在 C++ 的标准库中,所有的库函数和对象都封装在 std 命名空间中。

又由于默认不去命名空间进行搜索,在使用标准库时,可以选择指定访问,例如:

std::cout << "Hello World!" << std::endl;

或者展开空间:

using namespace std;

cout << "Hello World!" << endl;

需注意:这个展开并非展开头文件,因此,展开std不等于不包含头文件!!

在包含了头文件后,在预处理阶段会将其内容展开,而其内容是封在std里的,展开了std,等于告诉编译器可以去这些内容里面进行搜索。

但是,直接展开会有风险,如果自己定义的跟库重名,就会报错,这不就直接回到起点,将C语言的缺点再次包含。

因此,建议在项目中不要展开,而是指定访问;而日常练习中可以展开。

而在一个项目中,可能会有百万级的代码量,每次都指定访问太过麻烦,这里就出现了一种新的玩法:

指定展开

例如:

using std::cout;

using std::endl;

只需将常用的展开,不常用的再指定访问,就能大大减少工作量。

4.总结

命名空间是 C++ 中用于解决命名冲突的一个非常重要的工具。

在编写大型项目时,开发者应尽量避免直接展开整个命名空间,而是通过指定访问或部分展开的方式来使用命名空间。

合理使用命名空间不仅能提高代码的可维护性,还能避免命名冲突带来的问题。


希望本篇文章对你有所帮助!并激发你进一步探索编程的兴趣!

本人仅是个C语言初学者,如果你有任何疑问或建议,欢迎随时留言讨论!让我们一起学习,共同进步!



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。