C++——命名空间(namespace)

目录

1. C语言命名冲突

2. 命名空间定义

3. 命名空间使用

  • 可能大家在看别人写的C++代码中,在一开始会包这个头文件:#include<iostream>

这个头文件等价于我们在C语言学习到的#include<stdio.h>,它是用来跟我们的控制台输入和输出的,这里简要提下,后续详谈。

  • 除了上面这个头文件,还有这样一行代码:using namespace std;

namespace就是我们要接触C++的第一个关键字,它就是命名空间。

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

1. C语言命名冲突

 在正式引入namespace前,再来回顾下C语言的命名冲突问题:

#include<stdio.h>
//命名冲突
int rand = 0;
int main()
{printf("%d", rand);return 0;
}

如上的代码中,头文件我们只用了intclude<stdio.h>,暂无其它。我们定义了全局变量rand,并且代码可以正常编译没有任何错误。

但是要知道C语言存在一个库函数正是rand,但是要包上头文件#include<stdlib.h>,包上了这个头文件,再运行试试:

这里很明显发生命名冲突了,我们定义的全局变量rand和库里的rand函数冲突

想要解决此问题也非常简单,可能有人会说我修改变量名就可以了,确实可以,但并不是长久之计,如若我在不知情的状态下使用该变量超过100次,难道你要一个一个修改吗,这就充分体现了C语言的命名冲突。

在C++中,引入的命名空间namespace就很好解决了C语言的命名冲突问题。

2. 命名空间定义

定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{ }即可,{}中即为命名空间的成员。

如下:

 同一个作用域不能出现两个相同变量,此时的rand被关在n1的命名空间域里了,跟其它东西进行了隔离。所以在stdlib.h头文件展开时并不会发生命名冲突。此时rand的打印均是库函数里rand的地址,rand就是一个函数指针,打印的就是地址。

再比如:

此段代码更充分的体现了加上命名空间,不仅可以避免命名冲突,而且还告诉我们,此时再访问变量m、c、f,均是在全局域里访问的,而xzy这个命名空间域里的变量与全局域建立了一道围墙,互不干扰。不过这里c和m依旧是全局变量,命名空间不影响生命周期

命名空间有三大特性:

  • 1、命名空间可以定义变量,函数,类型
//1. 普通的命名空间
namespace N1 // N1为命名空间的名称
{// 命名空间中的内容,既可以定义变量,也可以定义函数,也可以定义类型int a; //变量int Add(int left, int right) //函数{return left + right;}struct ListNode //类型{int val;struct ListNode* next;}}
  • 2、命名空间可以嵌套
//2. 命名空间可以嵌套
namespace N2
{int a;int b;int Add(int left, int right){return left + right;}namespace N3{int c;int d;int Sub(int left, int right){return left - right;}}
}
  • 3、同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中
//3. 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。
namespace N1
{int Mul(int left, int right){return left * right;}
}

3. 命名空间使用

我们都清楚在C语言中,存在局部优先规则,如下:

int a = 0; //全局域
int main()
{int a = 1; //局部域printf("%d\n", a); // 1 局部优先return 0;
}

我们都清楚这里的结果是1,但是如若我非要打印全局域里的a呢?

这里引出域作用限定符 :: ),效果如下:

加上了( :: ) ,此时访问的a,就是全局域,这里是全局域的原因是“ ::”的前面是空白,如若是空白,那么访问的就是全局域,这么看的话,我在“ ::”前面换成命名空间域,不就可以访问命名空间域里的内容了吗。其实“ ::”就是命名空间使用的一种方式。

 比如我们定义了如下的命名空间:

namespace n1 
{int f = 0;int rand = 0;
}

现在该如何访问命名空间域里的内容呢?其实有3种方法:

  1. 加命名空间名称及作用域限定符“ ::”;
  2. 使用using namespace 命名空间名称全部展开;
  3. 使用using将命名空间中成员部分展开。
  • 1、加命名空间及作用域限定符“ ::”
int main()
{printf("%d\n", n1::f); //0printf("%d\n", n1::rand);//0return 0;
}

为了防止定义相同的变量或类型,我们可以定义多个命名空间来避免。

namespace ret
{struct ListNode{int val;struct ListNode* next;};
}namespace tmp
{struct ListNode{int val;struct ListNode* next;};struct QueueNode{int val;struct QueueNode* next;};
}

当我们要使用它们时,如下:

int main()
{struct ret::ListNode* n1 = NULL;struct tmp::ListNode* n2 = NULL;return 0;
}

针对命名空间的嵌套,如下:

 可以这样进行访问:

int main()
{struct tx::List::Node* n1; //访问List.h文件中的Nodestruct tx::Queue::Node* n2;//访问Queue.h文件中的Node
}

但是上述访问的方式有点过于麻烦,可不可以省略些重复的呢?比如不写tx::,这里就引出命名空间访问的第二种方法:

  • 2、使用using namespace 命名空间名称全部展开
using namespace tx;

这句话的意思是把tx这个命名空间定义的东西放出来,所以我们就可这样访问:

int main()
{struct List::Node* n1; //访问List.h文件中的Nodestruct Queue::Node* n2;//访问Queue.h文件中的Node
}

当然,我还可以再拆一层,如下:

using namespace tx;
using namespace List;
int main()
{struct Node* n1; //访问List.h文件中的Nodestruct Queue::Node* n2;//访问Queue.h文件中的Node
}

展开时要注意tx和List的顺序不能颠倒

这种访问方式是可以达到简化效果,但是也会存在一定风险:命名空间全部释放又重新回到命名冲突

 所以针对某些特定会出现命名冲突问题的,需要单独讨论:

由此我们得知:全部展开并不好,我们需要按需索取,用什么展开什么,由此引出第三种使用方法。

  • 3、使用using将命名空间中成员展开

针对上述代码,我们只放f出来:

namespace n1
{int f = 0;int rand = 0;
}
using n1::f;
int main()
{f += 2;printf("%d\n", f);n1::rand += 2;printf("%d\n", n1::rand);
}
  • 下面来看下C++的标准库命名空间:
#include<iostream>
using namespace std; //std 是封C++库的命名空间
int main()
{cout << "hello world" << endl; // hello worldreturn 0;
}

如若省去了这行代码:using namespace std;

想要输出hello world就要这样做:

#include<iostream>
int main()
{std::cout << "hello world" << std::endl;return 0;
}

当然也可以这样:

#include<iostream>
using std::cout;
int main()
{cout << "hello world" << std::endl;return 0;
}

这就充分运用到了命名空间。

 

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

echarts图表-实现中国地图的绘制

第一步&#xff1a;引入中国地图的json数据&#xff0c;初始化echarts的dom结构&#xff0c;使用registerMap方法加载地图数据&#xff08;注&#xff1a;echarts5.X版本后删除了中国地图的JSON&#xff0c;需自行下载JSON或降低echarts版本&#xff09; 第二步&#xff1a;传输…

暑期学JavaScript【第三天】

事件流 很多时候&#xff0c;一个事件可能会被多个元素监听。那么这些元素应该按什么顺序响应呢&#xff1f;从父到子称为捕获&#xff0c;从子到父称为冒泡。 事件捕获 可选参数&#xff0c;true or fause&#xff0c;带true代表按捕获方式响应 obj.addEventListener(cli…

AIGC下的CV多模态原理解析:从CLIP/BLIP到stable diffusion/Midjourney、GPT4

前言 终于开写本CV多模态系列的核心主题&#xff1a;stable diffusion相关的了&#xff0c;为何执着于想写这个stable diffusion呢&#xff0c;源于三点 去年stable diffusion和midjourney很火的时候&#xff0c;就想写&#xff0c;因为经常被刷屏&#xff0c;但那会时间错不…

Transformer回归预测

一、Attention is all you need——李沐论文精读Transformer 论文地址&#xff1a; https://arxiv.org/pdf/1706.03762.pdf Transformer论文逐段精读【论文精读】 卷积神经网络对较长的序列难以建模&#xff0c;因为他每次看一个比较小的窗口&#xff0c;如果两个像素隔得比较…

设计模式-05.01-行为型-观察者模板

观察者模式【常用】 我们常把 23 种经典的设计模式分为三类&#xff1a;创建型、结构型、行为型。前面我们已经学习了创建型和结构型&#xff0c;从今天起&#xff0c;我们开始学习行为型设计模式。我们知道&#xff0c;创建型设计模式主要解决“对象的创建”问题&#xff0c;…

大一下暑期计划 + 2023字节青训营预告直播

目录 &#x1f33c;前言 &#x1f339;后端学习方法 &#x1f333;1&#xff0c;层次 &#x1f333;2&#xff0c;体系 &#x1f333;3&#xff0c;算法和数据结构 &#x1f333;4&#xff0c;总结 &#x1f339;前端学习方法 &#x1f333;基础 &#x1f339;求职中如…

第三十五章Java面向对象概念及封装、继承、多态三种特性详解

面向对象简称 OO&#xff08;Object Oriented&#xff09;&#xff0c;20 世纪 80 年代以后&#xff0c;有了面向对象分析&#xff08;OOA&#xff09;、 面向对象设计&#xff08;OOD&#xff09;、面向对象程序设计&#xff08;OOP&#xff09;等新的系统开发方式模型的研究。…

Spring专家课程Day02_Spring-DI

文章目录 一、依赖注入_Autowired1.配置类中Bean 方式注入1.1&#xff09;注入实例1.2&#xff09;自动注入的匹配原则 2.组件扫描实现自动注入 Autowired3.set方法注入 二、接口解耦_自动注入规则1&#xff09;利用接口解耦2&#xff09;Autowired的注入规则3&#xff09;Qual…

Spring Boot中Elasticsearch的连接配置、原理与使用

Spring Boot中Elasticsearch的连接配置、原理与使用 引言 Elasticsearch是一种开源的分布式搜索和数据分析引擎&#xff0c;它可用于全文搜索、结构化搜索、分析等应用场景。在Spring Boot中&#xff0c;我们可以通过Elasticsearch实现对数据的搜索和分析。本文将介绍Spring …

Red Hat宣布限制访问源代码,尚有两种方式可获取

日前Red Hat宣布限制访问Red Hat Enterprise Linux(RHEL)源代码&#xff0c;而此举也将影响AlmaLinux 、Rocky Linux 等下游发行版。 随即Rocky Linux发布了名为《Keeping Open Source Open》的博文。文中是这样说的&#xff1a;    Red Hat的**ERP**服务条款 (TOS) 和最终…

驱动开发:内核读写内存多级偏移

让我们继续在《内核读写内存浮点数》的基础之上做一个简单的延申&#xff0c;如何实现多级偏移读写&#xff0c;其实很简单&#xff0c;读写函数无需改变&#xff0c;只是在读写之前提前做好计算工作&#xff0c;以此来得到一个内存偏移值&#xff0c;并通过调用内存写入原函数…

SpringBoot的日志

SpringBoot的日志 &#x1f50e;日志是什么&#x1f50e;日志的作用&#x1f50e;日志级别日志级别的作用日志级别的分类日志级别的设置 &#x1f50e;打印日志打印日志具体内容划分 &#x1f50e;常用的日志框架为什么这样设计对比System.out.ptintln()与日志框架 &#x1f50…