【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::cout还是A::cout,所以要想避免命名冲突你还是得显式的写std::coutA::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~


本篇文章的讲解就到这里了,如果有任何错处或者疑问欢迎大家评论区交流哦~~ 🙈 🙈

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基础篇:07-Nacos注册中心

1.Nacos安装部署 1.1 下载安装 nacos官网提供了安装部署教程&#xff0c;其下载链接指向github官网&#xff0c;选择合适版本即可。如访问受阻可直接使用以下最新稳定版压缩包&#xff1a;&#x1f4ce;nacos-server-2.1.0.zip&#xff0c;后续我们也可能会更改为其他版本做更…

图论学习(五)

极图 l部图的概念与特征 定义&#xff1a;若简单图G的点集V有一个划分&#xff1a; 且所有的Vi非空&#xff0c;Vi内的点均不邻接&#xff0c;设G是一个l部图。 如果l2&#xff0c;则G就是偶图。n阶无环图必是n部图。若l1<l2≤n&#xff0c;则任意的l1部图也是l2部图。…

【毕业设计】基于SpringBoot+Vue论坛管理系统【源码(完整源码请私聊)+论文+演示视频+包运行成功】

您好&#xff0c;我是码农飞哥&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精通 &#x1f601; 2. 毕业设计专栏&…

JavaScript学习笔记(7.0)

<!--* Author: RealRoad1083425287qq.com* Date: 2023-03-13 14:50:18* LastEditors: Mei* LastEditTime: 2023-03-13 15:08:54* FilePath: \vscode\鼠标跟随.html* Description: * * Copyright (c) 2023 by ${git_name_email}, All Rights Reserved. --> <!DOCTYPE …

Vue3(递归组件) + 原生Table 实现树结构复杂表格

一、递归组件 什么是递归&#xff0c;Javascript中经常能接触到递归函数。也就是函数自己调用自己。那对于组件来说也是一样的逻辑。平时工作中见得最多应该就是菜单组件&#xff0c;大部分系统里面的都是递归组件。文章中我做了按需引入的配置&#xff0c;所以看不到我引用组…

什么是让ChatGPT爆火的大语言模型(LLM)

什么是让ChatGPT爆火的大语言模型(LLM) 更多精彩内容: https://www.nvidia.cn/gtc-global/?ncidref-dev-876561 文章目录什么是让ChatGPT爆火的大语言模型(LLM)大型语言模型有什么用&#xff1f;大型语言模型如何工作&#xff1f;大型语言模型的热门应用在哪里可以找到大型语言…

西安石油大学C语言期末真题实战

很简单的一道程序阅读题&#xff0c;pa’默认为a【0】&#xff0c;接下来会进行3次循环 0 1 2 输出结果即可 前3题就是一些基础定义&#xff0c;在此不多赘述 要注意不同的数据类型的字节数不同 a<<2 b>>1&#xff08;b>>1;就是说b自身右位移一位&#xff08…

支付系统设计:消息重试组件封装

文章目录前言一、重试场景分析一、如何实现重试1. 扫表2. 基于中间件自身特性3. 基于框架4. 根据公司业务特性自己实现的重试二、重试组件封装1. 需求分析2. 模块设计2.1 持久化模块1. 表定义2. 持久化接口定义3. 持久化配置类2.2 重试模块1.启动2.重试3. 业务端使用1. 引入依赖…

Linux基础(3) Vim编辑器与Shell命令脚本

1、VIM文本编辑器 VIM编辑器的三大模式 命令模式&#xff1a; 控制光标移动&#xff0c;可对文本进行复制、粘贴和查找等工作输入模式&#xff1a; 正常的文本录入。末行模式&#xff1a; 保存或退出文档&#xff0c;以及设置编辑环境三种模式的切换&#xff1a; ​注意&…

app自动化测试——Android studio安装与配置

文章目录一、Appium框架介绍二、Appium 生态工具三、环境安装四、安装Android studio五、配置环境变量六、创建模拟器查看设备启动模拟器一、Appium框架介绍 1、跨语言&#xff1a;java、python等 2、跨平台&#xff1a;Android、IOS、Windows、Mac 3、底层多引擎切换 4、生态…

(待补充)小蒟蒻的刷题成长之路-------2023年中国高校计算机大赛-团队程序设计天梯赛(GPLT)上海理工大学校内选拔赛(同步赛)

小蒟蒻的刷题成长之路 蓝桥杯的比赛流程和必考点_蓝桥杯省赛考点_时雨h的博客-CSDN博客 大一学生一周十万字爆肝版C语言总结笔记_大一c语言笔记_时雨h的博客-CSDN博客 程序设计与 C 语言期末复习_时雨h的博客-CSDN博客 P8597 [蓝桥杯 2013 省 B] 翻硬币个人思考总结第五届传智杯…

西瓜视频登录页面

题目 代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><title>登录页面</title><style>td{width: 160px;height: 25px;}img{width: 20px;height: 20px;}.number, .password{background: rgba(0,0,0,.05);}.numbe…

指针进阶(上)

内容小复习&#x1f431;&#xff1a; 字符指针:存放字符的数组 char arr1[10]; 整型数组:存放整型的数组 int arr2[5]; 指针数组:存放的是指针的数组 存放字符指针的数组(字符指针数组) char* arr3[5]; 存放整型指针的数组(整型指针数组) int* arr[6]; 下面进入学习了哦~&…

【二分查找】

二分查找704. 二分查找35. 搜索插入位置34. 在排序数组中查找元素的第一个和最后一个位置结语704. 二分查找 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在…

mybatis中获取参数的两种方式:${}和#{}

目录 1.#{} 2.${} 3.总结 1.#{} 本质是占位符赋值 示例及执行结果&#xff1a; 结论&#xff1a;通过执行结果可以看到&#xff0c;首先对sql进行了预编译处理&#xff0c;然后再传入参数&#xff0c;有效的避免了sql注入的问题&#xff0c;并且传参方式也比较简单&#xf…

Python制作9行最简单音乐播放器?不,我不满足

人生苦短 我用python 好久不见啦~这次就来给大家整个大福利 ~ 源码资料电子书:点击此处跳转文末名片获取 最简单的9行代码音乐播放器如下&#xff1a; import time import pygamefile r歌曲路径 pygame.mixer.init() print(正在播放,file) track pygame.mixer.music.load(f…

计算机面试常见问答题目

英语口语 自我介绍 Hello, teachers. My name is Wang Xu. I come from Ningxia. I graduated from the School of Computer Science, Xi an Jiaotong University, majoring in Internet of Things. Next, I will introduce myself from four aspects. First of all, I studi…

Java开发 - ELK初体验

前言 前面我们讲过消息队列&#xff0c;曾提到消息队列也具有保存消息日志的能力&#xff0c;今天要说的EL看也具备这个能力&#xff0c;不过还是要区分一下功能的。消息队列的日志主要指的是Redis的AOF&#xff0c;实际上只是可以利用了消息队列来保存&#xff0c;却并不是消…

网络编程1(网络背景知识)

A给B发送消息如何保证数据一定能够发送到B的主机上&#xff0c;而不是其他地方 通过IP地址可以实现网络中制定的两个主机之间的通信&#xff0c;除此之外还要确定是哪个进程来处理&#xff0c;这里就用到端口&#xff08;port&#xff09; 端口—在一台主机上用于唯一标识一个…

MySQL索引特性

文章目录为什么要有索引&#xff1f;认识磁盘磁盘的结构磁盘的盘片结构定位扇区磁盘随机访问 (Random Access)与连续访问 (Sequential Access)MySQL与磁盘交互索引的理解测试主键索引索引的原理索引结构是否可以使用其他数据结构B树 vs B树聚簇索引 vs 非聚簇索引为什么要有索引…