C++的命名空间namespace详解及特殊情况分析

这里写目录标题

  • 历史来源
  • 意义
  • 定义
  • 使用
  • using namespace std弊端

历史来源

最开始的C++ 头文件仍然以.h为后缀,它们所包含的类、函数、宏等都是全局范围的。后来 C++ 引入了命名空间,计划重新编写库将类、函数、宏等都统一纳入一个命名空间std
但改版后的c++库致使旧c++库无法使用,在当时产生了巨大的反响,于是,C++ 开发人员想了一个好办法,保留原来的库和头文件,它们在 C++ 中可以继续使用,然后再把原来的库复制一份,把类、函数、宏等纳入命名空间 std 下,就成了新版 C++ 标准库。这样共存在了两份功能相似的库,使用了老式 C++ 的程序可以继续使用原来的库,新开发的可以使用新版的 C++ 库。
为了避免头文件重名,新版 C++ 库也对头文件的命名做了调整,去掉了后缀.h而对于原来C语言的头文件,也采用同样的方法,但在每个名字前还要添加一个c字母,stdio.h变成了cstdio,stdlib.h变成了cstdlib

意义

提到命名空间就不得不提到C语言了,由于C++是C语言的扩展,所以C++的开发人员就注意到C语言中命名冲突,例如一个团队的多个人员参与了一个文件管理系统的开发,他们都定义了一个全局变量 a,用来指明当前打开的文件,将他们的代码整合在一起编译时,很明显编译器会提示 重复定义(Redefinition)错误。

有一个常用的经典案例

#include<stdio.h>
#include<stdlib.h>
int rand = 0;//全局变量
int main()
{printf("%d\n", rand);return 0;
}       
// 出现了编译错误,error C2365: “rand”: 重定义;以前的定义是“函数”
//因为rand是一个C语言标注库中的函数,在编译时发现你使用的库中函数一样的名字就报错了

这个案例我们会在文章末尾进行再度分析

结论😗: 没有命名空间的话,在C/C++中,标识符(变量、函数和类)都是大量存在的,并且都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染.

定义

为了解决合作开发时的命名冲突问题,C++ 引入了命名空间Namespace的概念。
当你要定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{ }即可,{ }中即为命名空间的成员

①普通定义:

//项目中成员分别以自己的名字定义了命名空间
namespace zs//张三的变量定义
{  int a = 1;
}
namespace ls //李四的变量定义
{  int a = 2;
}

②嵌套定义


namespace demo1
{int a = 0;int Add(int left, int right){return left + right;}namespace demo2{int b = 0;}
}

③不同文件同名命名空间合并

// test.cpp
namespace N1
{int a;int b;int Add(int left, int right){return left + right;}
}// test.h
namespace N1
{int Mul(int left, int right){return left * right;}
}
//test.cpp 和 test.h中的 N1 最终会合并为一个

使用

正因为有了命名空间,所以使用对于的变量函数等,就要指明是具体哪个命名空间
在C++ 中 using 用于声明命名空间至全局命名空间(说白了就是解开std的束缚,让命名空间std完全暴露出来),使用命名空间也可以防止命名冲突。
:: 是一个新符号,称为域解析操作符,在C++中用来指明要使用的命名空间

1.命名空间名称+域解析操作符

//对应上面第一个普通定义的例子
zs::a = 10;
ls::a = 20;

2.using + 命名空间 :: 一个成员

using zs::a; //它的意思是,using 声明以后的程序中如果出现了未指明命名空间的 a//就使用 zs::a;但是若要使用ls定义的 a,仍然需要 ls::a。a = 10; //此时的a时zs命名空间
ls::a = 20;//ls命名空间

3.using namespace +命名空间

using namespace zs; //在 using 声明后,如果有未具体指定命名空间变量产生了命名冲突//那么默认采用命名空间 zs 中的变量。
a = 10; //此时的a时zs命名空间
ls::a = 20;//ls命名空间

对于上面的那个经典案例,我们就可以用到命名空间的知识进行解决

#include<iostream>
namespace hk
{int rand = 0;
};
int main()
{printf("%d", hk::rand); //用到第一种方法进行解决return 0; 
}

但是如果你使用第二种,第三种方法,就会出错
在这里插入图片描述
在这里插入图片描述

接下来我们就着重对这个例子进行分析
首先可能会有人有疑惑为什么 头文件是< iostream > ,并没有< cstdio >为什么也会找到< cstdio >里面的rand()函数

那是因为头文件的引用时进行了层层包含
iostraeam 里面引用了
#include < istream >
istream
引用了
#include < ostream >
引用了
#include < ios >
引用了
#include < xlocnum >
引用了
#include < cstdlib >
里面有如下定义
在这里插入图片描述

并且按照 C++ 的方式来使用C语言的头文件,即#include < cstdio >这种形式,那么符号可以位于命名空间 std 中,也可以位于全局范围中.
①.使用命名空间std

#include <cstdio>
int main()
{std::printf("kklovecode");return 0;
}

②.不使用命名空间 std

#include <cstdio>
int main()
{printf("kklovecode");return 0;
}   //在大部分编译器中都能通过

由此说明大部分编译器在实现时并没有严格遵循C++标准,它们对两种写法都支持,程序员可以使用 std 也可以不使用。
①写法是标准的,②不标准,虽然它们在目前的编译器中都没有错误,依然推荐使用①写法,因为标准写法会一直被编译器支持,非标准写法可能会在以后的不再支持。

所以由此可见,在使用C++的模式下使用C语言库里面的,编译器并没有严格按照C++的规范,所以就出现了上面经典案例中用第二种和第三种方法时,即使你根本没有手动使用using去声明这个命名空间(即命名空间暴露出来),但是仍然能够使用std里面的rand()函数.

using namespace std弊端

相信大家在看许多C++资料时,都会发现很多都是直接在头文件下面写上 using namespace std;用的就是第三种方法使用std命名空间
如:

#include<iostraeam>
using namespace std;
int main()
{...
}

因为标准库可能会升级,这样升级编译使用的C++版本的时候有可能因为引入了新的符号跟自己代码里的命名冲突,这样做增加了命名冲突的风险.
并且C++设置std命名空间就是不想你直接使用,而当你直接用using namespace std; 将其暴露出来,也多少有点违背C++本意.
总结😗:但一般来说,升级C++版本最多几年也就做一次,冲突的可能性也并不大,所以很多教材中的代码像那样使用节约时间成本也并非没有道理,但在中大型项目开发中是不被推荐的,适合使用第二种或者第三种方式

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

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

相关文章

信息系统安全运维模型 课堂记录

声明 本文是学习 信息系统安全运维管理指南. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 范围 本标准描述了信息系统安全运维管理体系&#xff0c;给出了安全运维策略、安全运维组织、安全运维规程和安全运维支撑系统等方面相关活动的目的、要求和…

【UE】简单的半透明+描边效果

效果 步骤 1. 首先新建一个材质&#xff0c;这里命名为“forcefield_mat” 打开“forcefield_mat”&#xff0c;混合模式选择半透明&#xff0c;着色模型选择无光照&#xff0c;勾选双面 连接如下节点 将材质给到一个球体&#xff0c;此时效果如下 如果想要相交时的描边效果&a…

MySQL数据库和表的操作

数据库基础 存储数据用文件就可以了&#xff0c;为什么还要弄个数据库? 文件保存数据有以下几个缺点&#xff1a; 1、文件的安全性问题 2、文件不利于数据查询和管理 3、文件不利于存储海量数据 4、文件在程序中控制不方便 数据库存储介质&#xff1a; 磁盘 内存 为了解决上…

【小沐学NLP】Python使用NLTK库的入门教程

文章目录 1、简介2、安装2.1 安装nltk库2.2 安装nltk语料库 3、测试3.1 分句分词3.2 停用词过滤3.3 词干提取3.4 词形/词干还原3.5 同义词与反义词3.6 语义相关性3.7 词性标注3.8 命名实体识别3.9 Text对象3.10 文本分类3.11 其他分类器3.12 数据清洗 结语 1、简介 NLTK - 自然…

如何修复老照片?老照片修复翻新的方法

老旧照片&#xff0c;尤其是黑白照片&#xff0c;往往因为年代久远、保存方式不当等原因而出现褪色、污损、划痕等问题&#xff0c;会比较难以修复&#xff0c;就算是技术精湛的专业修复师&#xff0c;也是需要投入极大时间精力的&#xff0c;效果也是不可预料的。 修复老照片…

Qt Creator 创建 Qt 默认窗口程序

Qt 入门实战教程&#xff08;目录&#xff09; Windows Qt 5.12.10下载与安装 使用Qt Creator 本文介绍用Qt自带的集成开发工具Qt Creator创建Qt默认的窗口程序。 本文不需要你另外安装Visual Studio 2022这样的集成开发环境&#xff0c;也不需要你再在Visual Studio 2022中…

设计模式系列-原型模式

一、上篇回顾 上篇创建者模式中&#xff0c;我们主要讲述了创建者的几类实现方案&#xff0c;和创建者模式的应用的场景和特点&#xff0c;创建者模式适合创建复杂的对象&#xff0c;并且这些对象的每 个组成部分的详细创建步骤可以是动态的变化的&#xff0c;但是每个对象的组…

ReactNative 井字游戏 实战

效果展示 需要的插件准备 此实战项目需要用到两个插件。 react-native-snackbar 底部信息提示组件。 react-native-vector-icons 图标组件。 安装组件&#xff1a; npm i react-native-snackbar npm i react-native-vector-icons npm i types/react-native-vector-icons /…

900ES1-0100 honeywell 可减少视觉引导应用的整体开发时间

900ES1-0100 honeywell 可减少视觉引导应用的整体开发时间 CV2视觉系统配有高柔性电缆(以太网或USB)。通过将高柔性电缆作为所有CV2视觉系统的标准配置&#xff0c;Epson CV2摄像机可以安装在机器人臂(移动)或固定装置(固定)上。基于向导的校准使机器人到视觉系统的校准变得轻…

docker介绍和安装

docker安装 下载Docker依赖组件 yum -y install yum-utils device-mapper-persistent-data lvm2 设置下载Docker的镜像源为阿里云 yum-config-manager --add-repo http://mirrors.aliyun.com/dockerce/linux/centos/docker-ce.repo 安装Docker服务 yum -y install docker-ce 安…

一探究竟:为什么需要 JVM?它处在什么位置?

小熊学Java全能学习面试指南&#xff1a;https://www.javaxiaobear.cn/ JVM我们并不陌生&#xff0c;现在我们就正式进入 JVM 的学习&#xff0c;如果你是一名软件开发工程师&#xff0c;在日常工作中除了 Java 这个关键词外&#xff0c;还有一个名词也一定经常被提及&#xf…

unity 场景烘培(边学习,边记录)

前言&#xff1a;好记性不如烂笔头&#xff0c;本文只提供参考&#xff01; 问题总结&#xff1a;1.unity 场景烘焙问题之模型UV有重叠_野区捕龙为宠的博客-CSDN博客 一、光源种类&#xff08;摘录&#xff1a;Unity灯光&#xff08;light&#xff09;_浮影℡的博客-CSDN博客…