C++ | 你真的了解namespace吗?

news2024/10/6 20:35:40

在这里插入图片描述

在这里插入图片描述

文章目录

  • 一、前言
  • 二、命名冲突
  • 三、命名空间
    • 1、域作用限定符
    • 2、命名空间的概念
      • 👉示例1
      • 👉示例2
    • 3、命名空间的定义
    • 4、命名空间的使用
      • ① 指定命名空间访问【做项目】
      • ② 使用using部分展开【做项目】
      • ③ 使用using namespace全局展开【日常练习】
    • 5、小结
  • 解答:为何使用using namespace std💡

一、前言

相信大部分在学校学习过C++的同学你们的老师一定会和你们说:现在要写C++的代码了,要换一下头文件用#include <iostream>,后面还要带上一个using namespace std;对于前一个头文件的包含和C语言中一样,若是需要使用对应库函数的话就要包一下这个头文件,但是你真的清楚后者吗?

今天我们就来聊聊有关C++中命名空间相关内容,带你好好见识一下什么叫做【命名空间】,并且带着using namespace std;到底是什么的问题好好地探索一番🔍

二、命名冲突

有关命名空间的说法,我们要先从命名冲突讲起

  • 若是我现在写了这么一段C语言代码,在main函数外定义了一个全局变量rand,然后在main函数中使用它,此时会发生什么呢?
#include <stdio.h>
#include <stdlib.h>

int rand = 0;

int main(void)
{
	printf("rand = %d\n", rand);
	return 0;
}
  • 运行起来我们可以发现出错了,这是为何呢?我也没有在什么地方定义过这个rand变量呀?其实它并不是和某个rand变量冲突了,而是库中的rand()函数发生了冲突

在这里插入图片描述

  • 通过查看cplusplus可以发现这个函数是包含在头文件<stdlib.h>中的,而我们上面刚好包含了这个头文件,在程序环境和预处理章节,有提到过对于头文件而言是会在预编译(预处理)阶段展开的,所以便会展开对rand()这个函数的定义,因而和我们定义的rand变量发生了冲突

在这里插入图片描述

  • 我们定义的变量不仅是会和库中的内容发生冲突,若是在公司中工作的时候还会发生和公司中其他同事之间发生冲突
  • 假设你的这个项目组写了一段代码上传远端仓库了,另一个项目组是和你们进行对接的,他们也将代码写完进行了上传,此时就有可能会发生定义的冲突

那要怎么去解决这个冲突呢?此时就可以使用到【namespace】命名空间了

三、命名空间

  • 命名空间(namespace)是C++语言特别重要的特性,当第三方供应商提供的库时,为了避免与其他供应商或者用户定义的名字相冲突(命名空间污染),常常将库的内容放置在自己独立的命名空间中。C++标准库也定义了相应命名空间std,用户在使用标准库时必须通过作用域运算符::,或者使用using关键词来简化命名空间中名字的使用

1、域作用限定符

在介绍namespace命名空间之前,我们先来看一个东西叫做【域作用限定符】

  • 首先你要清楚这个概念,域分为局部域和全局域,对于来说我们要关注的两点就是
    • 域内变量的使用
    • 域的生命周期

我通过下面这个代码来向你演示💻

int a = 0;
void f1()
{
	int a = 1;
	printf("a = %d\n", a);
}

void f2()
{
	int a = 2;
	printf("a = %d\n", a);
}

int main(void)
{
	f1();
	return 0;
}
  • 对于不同作用域的变量a来说,它所能使用的范围和生命周期都是不同的。对于在全局定义的变量a,它所使用的范围是全局,而对于两个函数f1和f2来说,它们所使用的范围则值局限于当前函数内
  • 对于生命周期也是一样,当一个局部变量出了其函数的作用域之外,当前函数的函数栈帧就销毁了,那么外界就无法访问到这个变量了
  • 通过观察,可以发现打印出来这个变量a为1,而不是0,这是为什么呢?因为它会优先寻找局部域中的a,而不是去全局找,若是局部没有的话再去全局找

在这里插入图片描述

  • 但我这个时候要强行去访问一下全局的变量a呢,有什么办法吗?那此时就可以使用到【域作用限定符】了

在这里插入图片描述

  • 可以看到,通过在变量a前面加上一个::,这个就叫做域作用限定符,因为这个符号的左边是空的,代表的就是全局域,所以会直接到全局去寻找这个变量a,此时打印出来的就不是这个1了,而不是0,这也就做到了访问当前局部作用域外的内容

2、命名空间的概念

有了的概念之后,我们再去学习命名空间就好多了,一起来看看吧👀

在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的

👉示例1

首先我们来看一个示例,也就是上面的那个rand变量的冲突

  • 定义命名空间很简单,只需要在namespace后面跟上命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。此时我们就可以将rand变量放到这个为 { r } 的命名空间中,那么它就不会和全局域中的其他内容发生冲突了,也就相当于是在荒野中盖了一间小屋子🏠,然后将一只羊🐏放在这个屋子中,狼🐺就很难轻易地发现它
namespace r 
{
	int rand = 0;
}
  • 那此时既然这个rand变量被保护起来了,我们还能访问到它吗?(可以使用域作用限定符,后面说💬)此时若是我直接去打印这个rand的话出来的结果就不是0了,而是一个随机数,此时它使用的其实就是库中的rand()

在这里插入图片描述

不过有同学会疑惑函数不是要加上()吗,为何这里的rand就可以直接调用这个函数了呢❓

在这里插入图片描述

  • 起初我也很疑惑,不过当我看到了编译器给出的Warning时,就知道了为什么了。若是我们直接使用了rand()这个函数的函数名rand,就相当于是获取到了这个函数的地址
  • 对于int(__cdecl *)(void)而言其实是一个函数指针,它指向了这个函数的地址,又刚好因为这个函数我们不需要传入参数,void表示无参,所以也相当于是调用了这个库函数【__cdecl是VS函数名修饰规则,我在函数重载会讲到】

在这里插入图片描述

👉示例2

同样,我们再通过一个示例来了解一下命名空间的概念

  • 我这里定义了两个.h的头文件,里面分别有队列和单链表的结构体,不过对于它们来说我都将结点的结构体名称定义成了Node,此时我若是在.cpp的源文件中同时包含了这两个头文件就会出现问题❌

在这里插入图片描述

#include "Queue.h"
#include "List.h"

int main(void)
{
	struct Node* qu;
	struct Node* l;

	return 0;
}
  • 通过运行代码可以看到,编译器报出了【类型重定义】的错误

在这里插入图片描述

  • 那这个时候应该怎么办呢?此时【命名空间】它又可以派上用场了👈
namespace Queue
{
	//队列结构体...
}
namespace SingleList
{
	//单链表结构体...
}
  • 可以看到,若是将它们都封装在各自的【命名空间】里,就不会出现重定义的现象了,那要如何使用各自命名空间中的内容呢?这个我们后面说

在这里插入图片描述

3、命名空间的定义

既然说了这是一个命名空间,那么在一个空间中就可以有很多的内容,而不仅限于一个普通的变量

  1. 变量/函数/结构体…
  • 可以看到,除了普通的变量之外,还可以在其内部定义函数、结构体等等
namespace r
{
	int rand = 0;

	int GetMax(int a, int b)	//函数
	{
		return a > b ? a : b;
	}
	
	struct Node {				//结构体
		struct Node* next;
		int val;
	};
}
  1. 命名空间嵌套定义
  • 不仅如此,在命名空间内还可以再嵌套定义命名空间
namespace r
{
	int rand = 0;

	int GetMax(int a, int b)
	{
		return a > b ? a : b;
	}
	
	struct Node {
		struct Node* next;
		int val;
	};

	namespace r2
	{
		int c;
		int d;
		int Sub(int left, int right)
		{
			return left - right;
		}
	}
}
  1. 命名空间合并
  • 既然我可以定义命名空间,那别人也可以定义,而且还可能会一模一样。若是我将上面提到过的队列结构体和单链表结构体所使用的结构体所存在的命名空间的名称设置为一样,那在同时包含List.hQueue.h的时候会出现什么情况呢?
  • 当有一个test.cpp中包含了这两个头文件的话两个命名空间中的内容会自动进行一个合并,也就是node这个命名空间中会有两个结构体,但是这个无法调试观察,所以不做过多解释

在这里插入图片描述

4、命名空间的使用

知道了命名空间该如何去定义,现在我们来说说如何去访问定义出来的命名空间的内容

① 指定命名空间访问【做项目】

【使用格式】:命名空间名称::成员

  • 一样是队列和链表的结构,现在它们的结构体名称一样,通过上面的学习可以知道这会发生冲突,于是我将它们分别放入了各自的命名空间中
namespace Queue
{
	struct Node {
		struct Node* next;
		int val;
	};
	//...
}
namespace SList
{
	struct Node {
		struct Node* next;
		int val;
	};
	//...
}
  • 可以看到,如果是这样的话就访问不到这两个结构体了,也无法定义结构体成员

在这里插入图片描述

  • 此时就需要通过指定命名空间去进行访问Queue::NodeSList::Node,也就需要使用到上面所讲的【域作用限定符 ::

在这里插入图片描述
⚠切记,不要像下面这么去用,因为是Node冲突了,而不是struct这个关键字冲突了

Queue::struct Node qu;
SList::struct Node sl;
  • 同理,若是要去访问命名空间内部的其他函数和普通变量的话,也可以这么去用
Queue::InitQueue(&qu);
Queue::Push(&qu, 1);
Queue::max = 200;

SList::PushBack(&sl, 1);
SList::PopBack(&sl);
SList::max = 300;

但是你不觉得这样很繁琐吗?对于一个队列或者是链表来说,初始化可能就使用到一次,但是入队和尾插就会被频繁使用到,此时我们就可以使用到using关键字来进行一个部分展开了

② 使用using部分展开【做项目】

【使用格式】:using 命名空间名称::成员

  • 我们可以将这个命名空间中常用的成员函数在头部进行using声明

using声明引入的名字的作用域满足一般的作用域规则:有效范围从using声明的地方开始,到其语句所在的作用域结束时为止

using Queue::Push;
using SList::PushBack;
using SList::PopBack;

在这里插入图片描述

  • 在面对C++中std标准库里的【cout】和【endl】时,我们就需要使用到using namespace std;,因为他们都定义在了标准库中,只是包含头文件<iostream>是不够的
  • 但若是你不想这么去写(这样写并不好),那就只能一个个地指定访问,可以看到这也是非常得繁琐,这要是在一个项目中有几百个输出那不得疯了🤪
#include <iostream>

int main(void)
{
	std::cout << "hello namespace" << std::endl;
	std::cout << "hello namespace" << std::endl;
	std::cout << "hello namespace" << std::endl;
	std::cout << "hello namespace" << std::endl;

	return 0;
}
  • 此时就可以使用到部分展开了,将我们需要常用的std中的【cout】和【endl】在开头首先声明一下,那么在后面就可以无限地使用了
using std::cout;
using std::endl;

在这里插入图片描述

在我们做项目的时候,就是使用上面的这两种方式配合使用,要频繁使用到std标准库或者自己定义的命名空间中的内容,那就定义在程序开头,若是不常用的就在代码行中指定访问一下

③ 使用using namespace全局展开【日常练习】

【使用格式】:using namespace 命名空间名称::成员

  • 对于这种方式而言就是既简单又粗暴,直接将Queue中的所有内容展开,而不需要再去一个个得进行指定了
using namespace Queue;

在这里插入图片描述

  • 但是对于这种方式其实是不好的,可以看出在上面我们进行【指定展开】和【部分展开】的时候没有出现问题,但是到了这里使用【全局展开】时,因为两个结构体的命名都是一样的,所以就发生了一个冲突

在这里插入图片描述
总结一下:

日常在做练习的时候,直接【全部展开】就可以了,方便使用;若是在做项目的时候,【指定展开】和【部分展开】配合使用,增加严谨性

5、小结

最后再来对本模块所学习的命名空间做一个总的回顾

  • 首先我们了解了什么是【域】的概念,也认识到了域作用限定符::的作用。对于两个头文件中的内容发生冲突时,就可以使用到namespace定义命名空间来进行一个封装,此时就不会发生冲突了
  • 但是将东西封装起来后,就很难去获取到它了,此时对于命名空间内成员的访问我介绍了三种方法,日常在做代码练习或者是OJ算法题的时候不要考虑【全局展开】的冲突问题;可以在做一个人项目或者在公司中做大型大项目的时候,就需要谨慎一些了,为了不和C++标准库std库中的内容进行冲突,就不要进行全局展开,而是应该【指定展开】和【部分展开】配合着用

解答:为何使用using namespace std💡

最后,我们再来解答一下文章开头提出的问题,为何我们要去使用到using namespace std这个东西呢❓相信在认真看完本文之后你心中一定也有了答案和自己使用时的取舍

  • 对于下面的这个,是C++中最标准的输出语句
#include <iostream>
using namespace std;

int main(void)
{
	cout << "hello namespace" << endl;
	return 0;
}
  • 可当我将using namespace std;去掉之后,【cout】和【endl】就报出了错误,这是为何呢?

在这里插入图片描述
看完下面这些你就会清楚了👇

  • cout和cin都是全局的流对象,endl是特殊的C++符号
  • 早期标准库将所有功能在全局域中实现,声明在.h后缀的头文件中,使用时只需包含对应头文件即可,后来将其实现在std命名空间下,为了和C头文件区分,也为了正确使用命名空间,规定C++头文件不带.h;旧编译器(vc 6.0)中还支持<iostream.h>格式,后续编译器已不支持,因此推荐使用+std的方式
  • 有了这句话之后,我们便可以百无禁忌┗|`O′|┛地使用标准库内定义的所有内容,也使得里面的内容全部暴露在了我们的眼前,安全性急剧下降

可是呢,这种命名空间直接展开我是不推荐的,不仅是对于标准std中的内容,以及我们自己所定义的命名空间也是一样,但是你要了解为什么要使用它。


📖参考文献📖

  1. C++命名空间namespace及using的使用
  2. C++/C++11中命名空间(namespace)的使用
  3. 为什么建议代码尽量不要使用 using namespace std

2023年2月27日记,如有疑惑,请指出

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/390945.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【经验分享】使用了6年的实时操作系统,是时候梳理一下它的知识点了 | 文末赠书4本

使用了6年的实时操作系统&#xff0c;是时候梳理一下它的知识点了 摘要&#xff1a; 本文简单介绍了博主学习操作系统的心路历程&#xff0c;同时还给大家总结了一下当下流行的几种实时操作系统&#xff0c;以及在工程中OSAL应该如何设计。希望对大家有所启发和帮助。 文章目录…

【开放域目标检测】一:Open-Vocabulary Object Detection Using Captions论文讲解

出发点是制定一种更加通用的目标检测问题&#xff0c;目的是借助于大量的image-caption数据来覆盖更多的object concept&#xff0c;使得object detection不再受限于带标注数据的少数类别&#xff0c;从而实现更加泛化的object detection&#xff0c;识别出更多novel的物体类别…

【数据结构】顺序表:尾部操作我很行,随机访问我超快!!!

顺序表的模拟实现 文章目录顺序表的模拟实现1.线性表2.顺序表2.1概念结构2.2顺序表的模拟实现2.2.1顺序表的初始化2.2.2顺序表的销毁2.2.3尾插数据2.2.4尾删数据2.2.5头插数据2.2.6头删数据2.2.7中间插入数据2.2.8中间删除数据2.2.9打印顺序表2.2.10查找数据2.2.11复用Insert和…

Linux学习第二十一节-sudo提权

1.概念 管理员提前为用户设置执行权限许可&#xff1b; 被授权用户有权执行授权命令&#xff1b; 配置文件&#xff1a;/etc/sudoers&#xff1b; 命令格式&#xff1a;sudo 特权命令。 2.提权操作 ①方式一vim编辑配置文件后wq&#xff01;&#xff1a;#vim /etc/sudo…

pnpm 基本详细使用(安装、卸载、使用)

一、简介 官网地址、GitHub地址、官方安装文档、官方卸载文档。 pnpm 全称 performant npm&#xff0c;意思为 高性能的 npm。pnpm 由 npm/yarn 衍生而来&#xff0c;解决了 npm/yarn 内部潜在的 bug&#xff0c;极大的优化了性能&#xff0c;扩展了使用场景。被誉为 最先进的…

【Docker】之docker-compose的介绍与命令的使用

&#x1f341;博主简介 &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01; 文章目录docker-compose简介docker-compose基础…

Word文档的密码忘记了怎么办?

Word文档可以设置两种密码&#xff0c;文件的“限制密码”和“打开密码”&#xff0c;今天来分享一下忘记这两种密码可以如何处理。 如果忘记的是Word文档的“限制密码”&#xff0c;文档就无法编辑及更改了&#xff0c;菜单目录中的相关选项也都是灰色状态&#xff0c;无法点…

LeetCode-343. 整数拆分

目录递归动态规划题目来源 343. 整数拆分 递归 对于给定的一个整数 n&#xff0c;穷举它的每一种分解情况&#xff0c;然后对所有情况&#xff0c;求最大值。 并且我们知道&#xff0c;n 可以拆成如下情况&#xff1a; 通过上图&#xff0c;我们很容易得到一个递归表达式&am…

Verilog使用always块实现时序逻辑

这篇文章将讨论 verilog 中一个重要的结构---- always 块&#xff08;always block&#xff09;。verilog 中可以实现的数字电路主要分为两类----组合逻辑电路和时序逻辑电路。与组合逻辑电路相反&#xff0c;时序电路电路使用时钟并一定需要触发器等存储元件。因此&#xff0c…

用Chrome浏览器加入新必应候补名单,微软最终还是无法赢得一切(~ ̄▽ ̄)~

前言 ChatGPT最近太火了&#xff0c;国内厂商刚开始宣布跟进时&#xff0c;微软必应搜索直接接入了ChatGPT的能力&#xff0c;推出了新必应。此举给Google当头一棒&#xff0c;相比于传统搜索引擎&#xff0c;新必应的这种搜索方式&#xff0c;让用户更快速的获取到自己问题的…

02 C语言计算

02 C语言计算 0、编程练习题 #include<stdio.h>int main(int argc,char const *argv[]){int a,b;scanf("%d %d",&a,&b);printf("%d %d %d\n",a,b,a b);printf("%d - %d %d\n",a,b,a - b);printf("%d * %d %d\n",a…

C/C++每日一练(20230305)

目录 1. 整数分解 ☆ 2. 二叉树的最小深度 ★★ 3. 找x ★★ 1. 整数分解 输入一个正整数&#xff0c;将其按7进制位分解为各乘式的累加和。 示例 1&#xff1a; 输入&#xff1a;49 输出&#xff1a;497^2示例 2&#xff1a; 输入&#xff1a;720 输出&#xff1a;720…

Java分布式事务(三)

文章目录&#x1f525;MySQL事务-MySQL中锁的分类&#x1f525;MySQL事务-MySQL中的死锁问题&#x1f525;MySQL事务-MySQL中锁的分类 MySQL中锁的分类 从本质上讲&#xff0c;锁是一种协调多个进程或多个线程对某一资源的访问的机制&#xff0c;MySQL使用锁和MVCC机制实现了…

TIA博途中将硬件目录更改为中文的具体方法演示

TIA博途中将硬件目录更改为中文的具体方法演示 基本步骤可参考如下: 第一步: 第二步: 具体的操作演示: 如下图所示,在所示的目录中找到zh-chs文件夹,删除或修改文件夹的名称均可,这里建议大家修改文件夹的名称,防止以后需要恢复成英文目录, 如下

3DEXPERIENCE Works 成为了中科赛凌实现科技克隆环境的催化剂

您的企业是否想过实现设计数据的统筹管理&#xff0c;在设计上实现标准化&#xff0c;并把每位设计工程师串联起来协同办公?中科赛凌通过使用3DEXPERIENCE Works 实现了上述内容&#xff0c;一起来看本期案例分享吧!中科赛凌 通过其自主研发的单压缩机制冷技术实现零下190℃制…

Hbase 的复制

HBase默认采用异步复制的方式同步数据&#xff0c;即客户端执行完put之后&#xff0c;RegionServer的后台线程不断地推送HLog的Entry到Peer集群。这种方式一般能满足大多数场景的需求&#xff0c;例如跨集群数据备份、HBase集群间数据迁移等。但是HBase 1.x版本的复制功能&…

【群晖Drive私有云】利用cpolar内网穿透实现公网远程群晖Drive

文章目录前言1.群晖Synology Drive套件的安装1.1安装Synology Drive套件1.2 设置Synology Drive套件1.3 局域网内电脑测试和使用2.使用cpolar远程访问内网Synology Drive2.1 Cpolar云端设置2.2 Cpolar本地设置2.3 测试和使用3. 结语前言 群晖作为专业的数据存储中心&#xff0…

路径规划 | 图解动态A*(D*)算法(附ROS C++/Python/Matlab仿真)

目录0 专栏介绍1 什么是D*算法&#xff1f;2 D*算法核心概念一览3 D*算法流程图4 步步图解&#xff1a;算法实例5 算法仿真与实现5.1 ROS C实现5.2 Python实现0 专栏介绍 &#x1f525;附C/Python/Matlab全套代码&#x1f525;课程设计、毕业设计、创新竞赛必备&#xff01;详…

【工具】logseq 使用分享

Github: https://github.com/logseq/logseq 三月八日国际劳动妇女节&#xff0c;当然要分享一款好用的记事本软件。 这次介绍的笔记本软件叫 logseq。 logseq 与传统的笔记软件不同&#xff0c;传统的笔记软件有各种数据单元&#xff08;post、title、refs、category、tags、…

智慧灌区信息化解决方案

系统概述智慧灌区信息化解决方案主要对对灌区的水情、雨情、土壤墒情、气象等信息进行监测&#xff0c;对重点区域进行视频监控&#xff0c;同时对泵站、闸门进行远程控制&#xff0c;实现了信息的测量、统计、分析、控制、调度等功能。为灌区管理部门科学决策提供了依据&#…