【C++】命名空间
🏖️作者:@malloc不出对象
⛺专栏:C++的学习之路
👦个人简介:一名双非本科院校大二在读的科班编程菜鸟,努力编程只为赶上各位大佬的步伐🙈🙈
目录
- 前言
- 一、命名空间产生的背景
- 二、命名空间的定义
- 三、命名空间的使用
- 四、从多个角度分析为什么不提倡使用using namespace
- 4.1 如何合理使用using namespace std
- 4.2 \<iostream> 与<iostream.h>的关系
- 五、如何合理使用命名空间来避免命名冲突
前言
本篇文章将给大家讲述的是命名空间,它是C++中为了解决命名冲突所引申出的一种解决方案,我们使用C++常用的using namespace std;std就是C++标准库的命名空间名称,也许有小伙伴兴许还不知道这句代码表示什么含义,下面就让我们进行命名空间的学习吧!!
一、命名空间产生的背景
我们首先来看一个例子:
C++是向下兼容C语言的语法的,在这段代码中使用rand发生了重定义,因为在C中rand是头文件stdlib.h中的一种库函数,而现在我们将rand作为普通变量名来使用这就造成了重命名,编译器不知道到底使用哪个rand,而在C语言中只能通过更换名称来解决这种命名冲突,可这其实是一个非常蛋疼的问题。
大型应用程序经常使用来自不同厂商的开发库,几乎不可避免会使用相同的名字,也就是说一个库中定义的名字可能与其他库中的名字相同而产生冲突。假如不同的程序员分别定义了类和函数,放在了不同的头文件中,在主函数的文件中需要使用这些类和函数时,就用#include指令将这些头文件包含进来,我们知道在预编译后#include头文件会将头文件的内容展开在源文件中,这样就在同一个程序文件中可能就会出现了多个名字相同的类或函数,这就造成了命名冲突,即在同一个作用域中有两个或者多个同名的实体,使得程序员不能组合各自独立的开发库到一个程序中。
对此ANSI C++引入的可以由用户命名的作用域,用来处理程序中常见的同名冲突。
命名空间是用来限定名字的解析和使用范围,它是C++开发大型程序的工具之一。命名空间的原理是将全局作用域划分为一个一个的命名空间,每个命名空间是一个独立的作用域,在不同命名空间内部定义的名字彼此之间互不影响,从而有效的避免了命名污染。
二、命名空间的定义
定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。
下面就是一个简单的命名空间:
命名空间可以在全局作用域或其他命名空间内部定义,但不能在函数、结构体或类内部定义,且要保证命名空间之间不会出现名字冲突。
在命名空间作用域内,可以包含:变量、对象以及它们的初始化、枚举常量、函数声明以及函数定义、类、结构体声明与实现、模板、其他命名空间。每个命名空间是一个作用域,定义在命名空间中的实体称为命名空间成员,命名空间中的每个名字必须是该命名空间中的唯一实体,但不同命名空间可以具有同名成员。
// 1. 正常的命名空间定义
namespace Curry1
{// 变量、函数、结构体....int a;void func1(){//...}struct Node{struct Node* next;int val;};//...
}// 2. 嵌套的命名空间定义
namespace Curry1
{// 变量、函数、结构体....int a;void func1(){//...}struct Node{struct Node* next;int val;};namespace Curry2{int b;void func2(){//...}}//...
}// 3.同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中
namespace Curry1
{// 变量、函数、结构体....int a;void func1(){//...}//...
}namespace Curry1
{int b;int Add(int a, int b){return a + b;}//...
}
注意:一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中。
三、命名空间的使用
命名空间有三种使用方式:
第一种使用方式:命名空间名称::xxx
这种命名空间的方式是三种之中最繁琐
的一种方式,因为它需要我们一个个去指定命名空间域,但同时它却是最安全
的一种方式,它避免了命名空间被污染。
这个地方还有一种使用域运算符引用全局命名空间的成员的方式,下面我们简单的看一个例子:
::xxx
表示引用全局命名空间成员,我们可以这样来简单的理解一下,域运算符左边为空就代表全局命名空间域。所以 ::xxx就可以表示直接引用xxx全局命名空间成员。
下面这里再补充一下嵌套命名空间的使用方式,我们一起来简单的看下:
第二种:using 命名空间名称::xxx
将std C++标准库中经常使用的少数几个成员使用using 命名空间名称::xxx部分展开,使用一部分暴露一部分,这样在一定程度上也减少了命名空间被污染,到后面我个人也是偏提倡这种使用命名空间的方式。
第三种:using namespace 命名空间名称
using namespace Curry1将Curry1命名空间域中的内容全局展开,所以我们的变量a以及Add函数可以直接进行使用,,这种方式最为方便直接令整个命名空间成员都有效,但同时这种全局展开的方式是最不安全的,下面我会带大家详细探究这个问题。
四、从多个角度分析为什么不提倡使用using namespace
1.首先我们这里特别强调尤其不要在头文件中使用using namespace。
我们知道#include头文件会在预处理阶段将头文件的内容展开到源文件中,如果将using namespace包含在头文件中那么随着会在源文件中全局展开,那么此时的命名空间就被污染了,换而言之就是使用using namespace全局展开之后命名空间域就消失了,我们在.cpp中写的代码很有可能会与using namespace命名空间域中的变量、函数、类等发生重名冲突。
在大工程中如果出现此类问题是很难去追查错误源头的,尤其像有多个文件嵌套包含了using namespace,你该如何去进行追查错误源头?这无疑为我们带来了巨大的维护成本。
上述关于尽量不要在头文件中使用using namespace,这就话本质就是在 “尽量不扩大using namespace xxx所影响到的域”,所以我们可以写在cpp文件中使其控制在一个编译单元内,也可以写在函数里控制在一个函数中,这样都一定程度上控制了using namespace所影响到的域的范围。
对于在源文件中使用using namespace,有一部分大佬则是持开放的态度认为源文件中可以随便在源文件中使用using namespace,因为.cpp文件不影响他人的使用;而对于这一点我的看法还是不能在任何情况下滥用using namespace,在源文件中也会有一定的风险,下面我们来看一个例子:
上图例子由于A和B的命名空间域都是全展开的,此时编译器就不确定变量b到底使用哪个命名空间的。
同样的角度,假设命名空间A中定义了一个函数Add_1,命名空间B中定义了一个函数Add_2,它们两者全局展开,,此时我们即可以使用函数Add_1又可以使用函数Add_2,但是以后假设A命名空间升级了也加入了一个函数Add_2,那么这时候我们原来的代码就有问题了,我们编译器到底使用的是命名空间A的Add_2还是命名空间B的Add_2呢?此时就发生了命名冲突,如果是一个大工程的话,假设命名空间A就是我们的C++标准命名空间,命名空间B是公司成员写的代码,此时C++标准命名空间升级了,那么此时这个公司之前写的代码就跑不过去了,这样就带来很大的损失…
2. 其次使用using namespace与命名空间设计的初衷背道而驰了。
本身namespace就是给你一个限定名字域的作用的,但是你using之后就相当于没有了命名空间域,因为using namespace将这个命名空间域全部展开暴露在文件中了嘛!!更通俗的来讲站在命名空间的角度使用using namespace跟没使用是一样的!!!
这里可能有读者还不明白,这里我举一个例子:当你用C++标准库STL的cout时,同时引用了命名空间域A中的cout,此时编译器不知道你要用的是std::cou
t还是A::cout
,所以要想避免命名冲突你还是得显式的写std::cout
与A::cout
,这样看来using namespace
是不是就没什么用了而且还带了一些麻烦。
Q:那么有读者可能会问那为啥要设计出using namespace这种使用命名空间的方式呢?
首先我认为设计者觉得在一些情况下使用using namespace是非常方便的,就如我们上个话题谈到的不涉及大量重名的、大型项目中的;其次也许设计者在设计时没考虑全展开重名呢,仅仅是觉得有些场景下使用起来方便所以设计出来的呢,,不管处于什么原因我认为存在即合理!!
总的来说,我认为不应该抹杀任何一种使用方式,我们只是站在不同的角度去辩证的看待这三种方式带来的问题。
4.1 如何合理使用using namespace std
通过上述我们对using namespace做的一系列分析,我们也知道要尽量使用using namespace全局展开,std是C++标准库的命名空间,如何展开std使用更合理呢?
1.在日常练习中,建议直接using namespace std即可,这样就很方便。
2.using namespace std展开,标准库就全部暴露出来了,如果我们定义跟库重名的类型/对象/函数,就存在冲突问题。该问题在日常练习中很少出现,但是项目开发中代码较多、规模大,就很容易出现,所以建议在项目开发中使用,像std::cout这样使用时指定命名空间 +using std::cout展开常用的库对象/类型等方式。
4.2 <iostream> 与<iostream.h>的关系
在我们之前的C语言学习当中,我们见到的想必都是.h头文件这种形式,但是在C++引入命名空间之后,C++新的标准为了和C区别开来规定头文件不使用后缀.h,另外C++为了兼容C,在C++标准化过程中,原有C语言头文件标准化后头文件名前带个c,例如cstdio、cstring、cstdlib等…
Q:那么在C++新的标准中<iostream>与<iostream.h>一样吗?
答案是不一样,这俩者是两个不同的文件,当使用<iostream.h>时相当于在C中调用库函数,使用的是全局命名空间,也就是早期C++的实现;当使用<iostream>时,该文件没有定义全局命名空间必须加上using namespace std;这样才能使用C++标准库中的类、函数等…这也就是我们经常写.cpp时通常头文件和using namespace std;缺一不可的原因。
五、如何合理使用命名空间来避免命名冲突
一般情况下,对偶尔使用命名空间的成员应该使用命名空间的作用域解析运算符
来直接给名称定位,这是最为安全的一种方式;而对一个大空间中经常要使用的少数几个命名空间成员提倡使用using 命名空间名称::
声明,我个人也是偏提倡一点这种使用方式,对于少数几个命名空间成员来讲它污染的命名空间域范围较小,因为毕竟第一种方式确实对于一个繁琐使用的少数命名空间成员不是很友好;对于最后一种using namespace
全展开命名空间域其实是最不提倡使用的,因为它会带来很多风险。但对于现阶段的我们来说,在平时的一些练习、竞赛以及项目小没有很多重名的时候,我们使用using namespace是可行的,因为它确实很方便并不需要我们过多去考虑命名冲突的风险,但使用using namespace终究还是有未知的风险的,我们不要在平时太依赖using namespace这种命名方式了。糖果虽甜,但吃多了就会有蛀牙🙈🙈
对于以后自己参加工作编写项目时,我个人的看法是将自己写的函数、类放在自己的命名空间,使用的时候不管是哪个命名空间里的,都要指明出处,都不要全部展开!!!因为你不知道什么时候风险会降临到你身边orz~
本篇文章的讲解就到这里了,如果有任何错处或者疑问欢迎大家评论区交流哦~~ 🙈 🙈
相关文章:

【C++】命名空间
🏖️作者:malloc不出对象 ⛺专栏:C的学习之路 👦个人简介:一名双非本科院校大二在读的科班编程菜鸟,努力编程只为赶上各位大佬的步伐🙈🙈 目录前言一、命名空间产生的背景二、命名空…...

【AutoSAR】【MCAL】Dio
一、结构 二、功能介绍 DIO(数字输入输出)驱动模块主要是对端口(Port),通道(Channel)和通道组(ChannelGroup)进行读写操作。 通道(Channel)&…...

瑞吉外卖——day2
目录 一、新增员工 二、查询分页数据 三、启用、禁用员工账户、编辑员工信息 一、新增员工 点击左上角新增员工 页面如下: 我们随便填数据 ,点击保存,请求的地址如下 返回前端可以看到请求方式为Post 在employeeController中编写对应的代…...

了解java
#常见编程语言介绍 C语言 C语言 java语言 javaScript语言 PHP语言 python语言Object-C和Swift语言 C# (c sharp)语言 Kotlin语言 Go语言 Basic语言 #JAVA的发展 起源于1991年SUN公司GREEN项目,1996年JDK1.0正式发布 后被Oracle公司收购&…...

【编程实践】代码之中有创意:“我一直认为工程师世界上最具创造性的工作之一”
代码之中有创意 “我一直认为工程师世界上最具创造性的工作之一”。 文章目录 代码之中有创意一、代码可以赋予创造力1.1 代码的创造力1.2 如何发挥代码的创造力二、有创意的代码可以提高工作效率2.1 代码创意可以提高工作效率2.2 如何利用代码创意来提高工作效率三、代码创意可…...

【MySQL】表连接
一、为什么要学习 因为不合理的使用连接会导致慢查询 二、什么是连接 参与连接的表叫做 连接表, 连接就是把 各个连接表 进行的组合 (笛卡儿积)加入结果集并返回 三、连接查询 如何只是对表进行大量的连接,笛卡儿积作用得到的…...
2023湖南省“楚怡杯”职业技能大赛“网络安全” 项目比赛任务书
2023湖南省“楚怡杯”职业技能大赛“网络安全” 项目比赛任务书2023安徽省“中银杯”职业技能大赛“网络安全” 项目比赛任务书A模块基础设施设置/安全加固(200分)A-1:登录安全加固(Windows, Linux)A-2:Ngi…...

Android应用启动优化笔记整理
应用启动相关流程与优化 应用启动主要涉及SystemServer进程 和 app进程。 SystemServer进程负责app进程创建和管理、窗口的创建和管理(StartingWindow 和 AppWindow)、应用的启动流程调度等。 App进程被创建后,进行一系列进程初始化、组件初…...
图像bytes字节串二进制转十六进制及bytes转为图像
目录前言正文二进制与十六进制的bytes互转读取bytes为图像法1:直接写入f.read的结果法2: 转换为PIL或Numpy前言 参考: 8. python基础之基础数据类型–bytes - CSDN python 16进制与图片互转 - CSDN 正文 二进制与十六进制的bytes互转 bytes保存的是原始的字节(二…...

信息安全与数学基础-笔记-②同余
知识目录同余完全剩余系剩余类完全剩余系❀简化剩余系❀欧拉函数逆元!欧拉定理 !同余 a,b 两个数字,都模m,当两个数字模m后余的数一样即为同余。 例子: a bq r (mod m),这里的a 和 r 就是同余 ÿ…...
网络安全法
目录正文第一章第二章第三章第四章第五章第六章 法律责任第七章 附则正文 学习网络安全应该知道网络安全法 第一章 总则 第一条: 为了保障网络安全,维护网络空间主权和国家安全、社会公共利益,保护公民、法人和其他组织的合法权益,促进经济…...

django框架开发部署项目
前言:相信看到这篇文章的小伙伴都或多或少有一些编程基础,懂得一些linux的基本命令了吧,本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python:一种编程语言&…...
Unity记录1.3-入门-第一阶段总结
文章首发及后续更新:https://mwhls.top/4447.html,无图/无目录/格式错误/更多相关请至首发页查看。 新的更新内容请到mwhls.top查看。 欢迎提出任何疑问及批评,非常感谢! 汇总:Unity 记录 摘要:第一阶段的总…...
Linux入门篇-文件管理
简介 简单的文件管理。 ⽂件内容的查看 ⽂本⽂件内容的查看 cat ⽂本⽂件的path1 ⽂本⽂件的path2 head ⽂本⽂件的path ,显示⽂件的前10⾏内容 head -n 5 ⽂本⽂件的path , 显示⽂件的前5⾏内容 head -5 等于head -n 5tail ⽂本⽂件的path, 显示⽂件的后10⾏内容…...

如何从错误中成长?
在上一篇文章“技术人的犯错成本”里,我和你聊了技术人可能会犯的各式各样的错误,也举了很多例子,说明了技术人犯错的成本。在竞争激烈的互联网时代,试错当然是好事,但了解错误成本,避免不应该犯的错误&…...
谈谈一个程序员的职场心得(真有用)
谈谈一个程序员的职场心得 我会分为三个部分:软件开发,职场协作和认知成长,每个部分精简成 7 条心得。 软件开发 若无必要,勿增实体。 这是奥卡姆剃刀的定义,所谓剃刀就是法则,是奥卡姆这个英国学者提出来…...
Pytest:一个卓有成效的测试工具
大家都知道,目前最流行的Python单元测试框架有三种,分别是unittest, nose和pytest。其中unittest是Python自带的测试框架,但问题是比较老了,赶不上时代发展了(哈哈哈);nose2定位是带插件的unitt…...

Compose 动画 (三) : AnimatedVisibility 从入门到深入
1. AnimatedVisibility 是什么 AnimatedVisibility可以实现Compose组件的显示和隐藏,并且可以指定显示/隐藏时候的动画效果。(EnterTransition/ExitTransition) 和 animateXxxAsState、animateContentSize、Crossfade、AnimatedContent 这几个API一起,都…...

网络基础(二)
目录 应用层 再谈 "协议" 协议是一种 "约定". socket api的接口, 在读写数据时, 都是按 "字符串" 的方式来发送接收的. 如果我们要传输一些"结构化的数据" 怎么办呢? 为什么要转换呢? 如果我们将struct message里面…...

Java线程知识点总结
文章目录Java 线程基础线程简介什么是进程什么是线程进程和线程的区别创建线程ThreadRunnableCallable、Future、FutureTaskCallableFutureFutureTaskCallable Future FutureTask 示例线程基本用法线程休眠线程礼让终止线程守护线程线程通信wait/notify/notifyAlljoin管道线程…...
利用ngx_stream_return_module构建简易 TCP/UDP 响应网关
一、模块概述 ngx_stream_return_module 提供了一个极简的指令: return <value>;在收到客户端连接后,立即将 <value> 写回并关闭连接。<value> 支持内嵌文本和内置变量(如 $time_iso8601、$remote_addr 等)&a…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...

九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...
WEB3全栈开发——面试专业技能点P7前端与链上集成
一、Next.js技术栈 ✅ 概念介绍 Next.js 是一个基于 React 的 服务端渲染(SSR)与静态网站生成(SSG) 框架,由 Vercel 开发。它简化了构建生产级 React 应用的过程,并内置了很多特性: ✅ 文件系…...
Java并发编程实战 Day 11:并发设计模式
【Java并发编程实战 Day 11】并发设计模式 开篇 这是"Java并发编程实战"系列的第11天,今天我们聚焦于并发设计模式。并发设计模式是解决多线程环境下常见问题的经典解决方案,它们不仅提供了优雅的设计思路,还能显著提升系统的性能…...
Django RBAC项目后端实战 - 03 DRF权限控制实现
项目背景 在上一篇文章中,我们完成了JWT认证系统的集成。本篇文章将实现基于Redis的RBAC权限控制系统,为系统提供细粒度的权限控制。 开发目标 实现基于Redis的权限缓存机制开发DRF权限控制类实现权限管理API配置权限白名单 前置配置 在开始开发权限…...