【Cpp】命名空间
前言
在C语言中,命名冲突通常发生在不同的作用域中使用了相同的标识符:
-
全局变量和局部变量同名: 如果在全局作用域和局部作用域中都定义了同名的变量,那么在局部作用域中,全局变量会被局部变量遮蔽。
int globalVar; // 全局变量void someFunction() {int globalVar = 10; // 局部变量,遮蔽了全局变量 }
-
函数参数和局部变量同名: 如果在函数参数列表中定义了一个参数,然后在函数体中又定义了一个同名的局部变量,那么参数会被局部变量遮蔽。
void someFunction(int localVar) {int localVar = 20; // 局部变量,遮蔽了函数参数 }
-
宏定义和变量名冲突: 如果使用
#define
定义了一个宏,而这个宏的名字和某个变量名相同,那么在宏展开时可能会导致命名冲突。#define MAX 100 // 宏定义int MAX; // 变量定义,与宏定义冲突
-
不同文件中的全局变量同名: 如果在不同的源文件中定义了同名的全局变量,并且这些文件被编译到同一个程序中,那么链接时可能会发生命名冲突。
// file1.c int globalVar;// file2.c int globalVar;
-
结构体和变量名冲突: 如果在同一个作用域中定义了一个结构体和一个同名的变量,那么结构体的名称会被变量名遮蔽。
struct MyStruct {int a; };struct MyStruct myVar; // 变量名,遮蔽了结构体名struct MyStruct *ptr; // 错误:这里会报错,因为MyStruct被遮蔽了
-
typedef和变量名冲突: 如果使用
typedef
定义了一个类型,而这个类型的名字和某个变量名相同,那么在定义变量时可能会导致命名冲突。typedef int MyInt;MyInt myVar; // 变量定义int MyInt; // 错误:这里会报错,因为MyInt被遮蔽了
为了解决这一问题,Cpp添加了一个特性,namespace,命名空间,也有称作名字空间,通过namespace的特性来减少对变量名的污染。
文章目录
- 前言
- 命名空间
- 创建一个命名空间
- 未命名的命名空间
- 友元
- 使用命名空间
- 作用域解析
- 使用指令
- 使用声明
- 命名空间的使用
- 小结
命名空间
把一个全局命名空间分成多个可管理的小空间。关键字namespace,如同class、struct、enum和union一样,把它们的成员的命名放到了不同的空间中去,与其他的关键字目的不同的是,namespace唯一一的目的是产生一个新的命名空间。
创建一个命名空间
创建一个命名空间与创建一个类非常相似:
// C10:MyLib.cpp
namespace MyLib {// Declarations
}
int main() {}
这就产生了一个新的命名空间,其中包含了各种声明。然而,namespace与class、struct、union和enum有明显的区别:
- namespace只能在全局范围内定义,但它们之间可以互相嵌套。
- 在namespace定义的结尾,右花括号的后面不必跟一个分号。
- 一个namespace可以在多个头文件中用一个标识符来定义,就好像重复定义一个类一样。
// C10:Header1.h
#ifndef HEADER1_H
#define HEADER1_H
namespace MyLib {extern int x;void f();
}
#endif // HEADER1_H
// C10:Header2.h
#ifndef HEADER2_H
#define HEADER2_H
#include "Header1.h"
// Add more names to MyLib
namespace MyLib { // NOT a redefinition!extern int y;void g();
}
#endif // HEADER2_H
// C10:Continuation.cpp
#include "Header2.h"
int main() {}
- 一个namespace的命名可以用另一个命名来作它的别名,这样就不必敲那些开发商提供的冗长的命名了。
// C10:BobsSuperDuperLibrary.cpp
namespace BobsSuperDuperLibrary {class Widget { /*...*/ };class Poppit { /*...*/ };//...
}
// Too much to type! I'll alias it:
namespace Bcb = BobsSuperDuperLibrary;
int main() {}
未命名的命名空间
每个翻译单元都可包含一个未命名的命名空间——可以不用标识符而只用“namespace”再加一个命名空间。
// C10:UnnamedNamespaces.cpp
namespace {class Arm { /*...*/ };class Leg { /*...*/ };class Head { /*...*/ };class Robot {Arm arm[4];Leg leg[16];Head head[3];//...};
}
int main() {}
在这个空间中的命名自动地在翻译单元内无限制地有效。但要确保每个翻译单元只有一个未命名的命名空间。如果把一个局部命名放在一个未命名的命名空间中,不需要加上static说明就可以让它们作内部连接。
友元
可以在一个命名空间的类定义之内插入(inject)一个友元(friend)声明:
// C10:FriendInjection.cpp
namespace Me {class Us {//...friend void you();};
}
int main() {}
这样函数you()就成了命名空间Me的一个成员。
使用命名空间
在一个命名空间中引用一个命名可以采取三种方法:第一种方法是用作用域运算符,第二种方法是用using指令把所有命名引入到命名空间中,第三种方法是用using声明一次性引用命名。
作用域解析
命名空间中的任何命名都可以用作用域运算法则明确地指定,就像引用一个类中的命名一样,学过java的人员也会想到一个包package的概念,namespace也像package一样,但:
- Java 的 package 与文件系统结构紧密相关,而 C++ 的 namespace 则不强制要求与文件系统结构对应。
- 访问控制:Java 的 package 通过访问修饰符和 package 声明来控制访问权限,而 C# 和 C++ 的 namespace 主要用于组织代码,访问控制由访问修饰符决定。
// C10:ScopeResolution.cpp
namespace X {class Y {static int i;public:void f();};class Z {void func();public:int X::Y::i = 9;class X::Z {int u, v, w;public:Z(int i);int g();};int X::Z::g() { return u = v = w = 0; }void X::func() {Z a(1);a.g();}}int main() {}
}
注意定义X::Y::i就像引用一个类Y的数据成员一样容易,Y如同被嵌套在类X中而不像是被嵌套在命名空间X中。
到目前为止,命名空间看上去很像类。
使用指令
用using关键字可以让我们立即进入整个命名空间,摆脱输入一个命名空间中完整标识符的麻烦。这种using和namespace关键字的搭配使用称为使用指令(using directive)。using关键字声明了一个命名空间中的所有命名是在当前范围内,所以可以很方便地使用这些未限定的命名。如果以一个简单的命名空间开始:
// C10:NamespaceInt.h
#ifndef NAMESPACEINT_H
#define NAMESPACEINT_H
namespace Int {enum sign { positive, negative };class Integer {int i;sign s;public:Integer(int ii = 0) : i(ii), s(i >= 0? positive : negative) {}sign sign() const { return s; }};
}
#endif // NAMESPACEINT_H
// C10:NamespaceOverriding1.cpp
#include "NamespaceMath.h"
int main() {using namespace Math;Integer a; // Hides Math::aa.setSign(negative);// Now scope resolution is necessary// to select Math::a:Math::a.setSign(positive);
}
如果有第二个命名空间,它包含命名空间Math的某些命名:
// C10:NamespaceOverriding2.cpp
#ifndef NAMESPACEOVERRIDING2_H
#define NAMESPACEOVERRIDING2_H
#include "NamespaceInt.h"
namespace Calculation {using namespace Int;Integer divide(Integer, Integer);
}
#endif // NAMESPACEOVERRIDING2_H
因为这个命名空间也是用using指令来引入的,这样就可能产生冲突。不过,这种二义性出现在命名的使用时,而不是在using指令使用时。
// C10:OverridingAmbiguity.cpp
#include "NamespaceMath.h"
#include "NamespaceOverriding2.h"
void s() {using namespace Math;using namespace Calculation;// Everything's ok until:// divide(1, 2); // Ambiguity
}
int main() {}
这样,即使永远不产生歧义性,使用using指令引入带命名冲突的命名空间也是可能的。
使用声明
可以用使用声明(using declaration)一次性引入到当前范围内。这种方法不像using指令那样把那些命名当成当前范围的全局名来看待,using声明是在当前范围之内进行的一个声明,这就意味着在这个范围内它可以不顾来自using指令的命名。
// C10:UsingDeclaration.h
#ifndef USINGDECLARATION_H
#define USINGDECLARATION_H
namespace U {inline void f() {}inline void g() {}
}
namespace V {inline void f() {}inline void g() {}
}
namespace V {inline void f() {}inline void g() {}
}
#endif // USINGDECLARATION_H
// C10:UsingDeclaration1.cpp
#include "UsingDeclaration.h"
void h() {using namespace U; // Directiveusing V::f; // Declarationf(); // Calls V::f();U::f(); // Must fully qualify to call
}
// C10:UsingDeclaration2.cpp
#include "UsingDeclaration.h"
void m() {using namespace Q;f(); // Calls U::f();g(); // Calls V::g();
}
int main() {}
一个using声明是一个别名,它允许在不同的命名空间声明同样的函数。如果不想由于引入不同命名空间而导致重复定义一个函数时,可以使用using声明,它不会引起任何二义性和重复。
命名空间的使用
上面所介绍的一些规则刚开始时也许会使我们感到气馁,特别是当我们知道将来一直使用它们会有什么感觉时,尤其如此。一般说来,只要真正理解了它们的工作机理,使用它们也会变得非常简单。需要记住的关键问题是当引入一个全局using指令时(可以在任何范围之外通过使用namespace),就已经为那个文件打开了该命名空间。对于一个实现文件(一个.cpp文件)来说,这通常是一个好方法,因为只有在该文件编译结束时,using指令才会起作用。也就是说,它不会影响任何其他的文件,所以可以每次在一个实现文件中调整对命名空间的控制。 例如,如果发现由于在一个特定的实现文件中使用太多的using指令而产生命名冲突,就要对该文件做简单的改变,以致使用明确的限定或者using声明来消除命名冲突,这样不用修改其他的实现文件。
头文件的情况与此不同。不要把一个全局的using指令引入到一个头文件中,因为那将意味着包含这个头文件的任何其他头文件也会打开这个命名空间(头文件可以被另一个头文件包含)。
所以,在头文件中,最好使用明确的限定或者被限定在一定范围内的using指令和using声明。在本专栏中将讨论这种用法,通过这种方法,就不会“污染”全局命名空间和退到C++的命名空间引入前的世界。
小结
虽然命名可以嵌套在类中,但全局函数、全局变量以及类的命名还是在同一个全局命名空间中。虽然static关键字可以使变量和函数进行内部连接(使它们文件静态,从而做到一定的控制。但在一个大项目中,如果对全局的命名空间缺乏控制就会引起很多问题。为了解决这些问题,开发商常常使用冗长、难懂的命名,以使冲突减少,但这样我们不得不一个一个地敲这些命名(typedef常常用来简化这些命名),namespace的要求也就应运而生,它可以使我们的变量名称和函数名称变得更短,虽然不一定清晰,但是可以在一定程度上减少冲突。利用using 指令的时候,我们要考虑是否会对该变量名称是否会对后续的程序产生一定的影响,毕竟using指令是全局作用产生的,如果会对后续程序产生影响,我们就用作用域解析,这时候就像一个类一样,虽然说稍稍麻烦了一些,但是我们还是可以解决命名冲突的问题————毕竟这就是namespace这一特性存在的原因。
相关文章:

【Cpp】命名空间
前言 在C语言中,命名冲突通常发生在不同的作用域中使用了相同的标识符: 全局变量和局部变量同名: 如果在全局作用域和局部作用域中都定义了同名的变量,那么在局部作用域中,全局变量会被局部变量遮蔽。 int globalVar; // 全局变量…...

ESP32学习笔记——LOG日志库的使用
注:本文由CHATGPT辅助创作,未经验证,实际工程使用请仔细甄别。 对于设置日志级别的几种方式(esp_log_level_set、CONFIG_LOG_DEFAULT_LEVEL、CONFIG_LOG_MAXIMUM_LEVEL、LOG_LOCAL_LEVEL )容易混淆,特此学习…...

51c~C语言~合集1
我自己的原文哦~ https://blog.51cto.com/whaosoft/12428240 一、C语言和C的区别 C语言虽说经常和C在一起被大家提起,但可千万不要以为它们是一个东西。现在我们常用的C语言是C89标准,C是C99标准的。C89就是在1989年制定的标准,如今最新…...

$nextTick 实现原理
Vue 使用 nextTick 来确保数据更新后的 DOM 操作在更新完成后执行。其核心逻辑是将回调放到微任务或宏任务队列中,确保回调在 DOM 更新完成后执行。 Vue.js 会利用不同的浏览器 API 来模拟 nextTick 的延迟执行,通常是通过: Promise&#x…...

kelp protocol
道阻且长,行而不辍,未来可期 有很长一段时间我都在互联网到处拾金,but,东拼西凑的,总感觉不踏实,最近在老老实实的看官方文档 & 阅读白皮书 &看合约,挑拣一些重要的部分配上官方的证据,和过路公主or王子分享一下,愿我们早日追赶上公司里那些可望不可及大佬们。…...

Golang--面向对象
Golang语言面向对象编程说明: Golang也支持面向对象编程(OOP),但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言。所以我们说Golang支持面向对象编程特性是比较准确的。Golang没有类(class),Go语言的结构体(struct)和其…...

深度学习经典模型之LeNet-5
1 LeNet-5 1.1 模型介绍 LeNet-5是由 L e C u n LeCun LeCun 提出的一种用于识别手写数字和机器印刷字符的卷积神经网络(Convolutional Neural Network,CNN) [ 1 ] ^{[1]} [1],其命名来源于作者 L e C u n LeCun LeCun的名字…...

Abaqus随机骨料过渡区孔隙三维网格插件:Random Agg ITZ Pore 3D (Mesh)
插件介绍 Random Agg ITZ Pore 3D (Mesh) V1.0 - AbyssFish 插件可在Abaqus内参数化建立包含水泥浆基体、粗细骨料、界面过渡区(ITZ)、孔隙在内的多相材料混凝土细观背景网格模型。 模型说明 插件采用材料映射单元的方式,将不同相材料赋值…...

PG数据库 jsonb字段 模糊查询
背景: 项目由于多语言的设计,将字段设置成json字段类型,同时存储中文和英文 页面上通过输入框实现模糊的查询 一、表结构:name字段设置jsonb类型 二、表数据 3、Mybatis编写sql select pp.name ->>zh-CN as pmsProductNam…...

javascript-Web APLs (四)
日期对象 用来表示时间的对象 作用:可以得到当前系统时间 在代码中发现了 new 关键字时,一般将这个操作称为 实例化 //创建一个时间对象并获取时间 //获得当前时间 const date new Date() //获得指定时间 const date new Date(2006-6-6) console.log(…...

Keras 3 示例:开启深度学习之旅
Keras 3 示例:开启深度学习之旅 一、Keras 3 简介 Keras 3是一个强大的深度学习框架,它为开发者提供了简洁、高效的方式来构建和训练神经网络。它在之前版本的基础上进行了改进和优化,具有更好的性能、兼容性和功能扩展性。无论是初学者还是…...

鸿蒙Next如何接入微信支付
大家好,这是我工作中接触到的鸿蒙Next接入微信支付,有使用到,分享给大家,轻松便捷 前提:你已有鸿蒙版本的微信,并且微信余额或绑定银行卡有钱,因为内测的微信暂不支持收红包和转账,2.你的应用已…...

nginx(五):关于location匹配规则那些事
关于location匹配规则那些事 1 概述2 语法3 匹配规则说明3.1 精确匹配3.2 前缀匹配(^~)3.3 正则表达式匹配(\~和\~*)3.4 普通前缀匹配 4 匹配优先级5 注意事项6 总结 大家好,我是欧阳方超,可以我的公众号“…...

【论文阅读】Associative Alignment for Few-shot Image Classification
用于小样本图像分类的关联对齐 引用:Afrasiyabi A, Lalonde J F, Gagn C. Associative alignment for few-shot image classification[C]//Computer Vision–ECCV 2020: 16th European Conference, Glasgow, UK, August 23–28, 2020, Proceedings, Part V 16. Spri…...

acmessl.cn提供接口API方式申请免费ssl证书
目录 一、前沿 二、API接口文档 1、证书可申请列表 简要描述 请求URL 请求方式 返回参数说明 备注 2、证书申请 简要描述 请求URL 请求方式 业务参数 返回示例 返回参数说明 备注 3、证书查询 简要描述 请求URL 请求方式 业务参数 返回参数说明 备注 4、证…...

DBeaver如何快速格式化sql语句,真简单!
前言 我之前在使用DBeaver的时候,一直不知道其可以格式化sql语句,导致sql语句看起来比较杂乱,今天就来介绍下DBeaver如何格式化sql语句。 如何格式化sql语句 首先,我们打开一个sql窗口,在里面输入我们要查询的sql语…...

OpenCV C++ 计算两幅图像之间的多尺度结构相似性(MSSIM)
目录 一、定义与背景 二、计算流程 三、性质与特点 四、应用场景 五、代码实现 多尺度结构相似性(MSSIM)是一种用于衡量两幅图像之间相似度的指标,它基于结构相似性(SSIM)指数进行扩展,通过在不同尺度上计算SSIM来评估图像的整体质量。以下是对MSSIM的详细介…...

代码随想录第二十二天
回溯算法理论介绍 回溯算法是一种基于递归思想的算法设计技术,适用于解决需要构造所有解或找到特定解的组合问题。回溯的基本思路是通过系统地搜索所有可能的解决方案,然后逐步撤销不符合要求的选择,回到上一步继续尝试。这种算法最适合应用…...

【k8s】ClusterIP能http访问,但是不能ping 的原因
ClusterIP 服务在 Kubernetes 中是可以访问的,但通常无法通过 ping 命令来测试连通性。这主要是因为 ClusterIP 是一个虚拟 IP 地址,而不是实际分配给某个网络接口的 IP 地址。以下是一些原因和解释: 1. 虚拟 IP 地址 ClusterIP 是一个虚拟…...

【力扣打卡系列】单调栈
坚持按题型打卡&刷&梳理力扣算法题系列,语言为go,Day20 单调栈 题目描述 解题思路 单调栈 后进先出 记录的数据加在最上面丢掉数据也先从最上面开始 单调性 记录t[i]之前会先把所有小于等于t[i]的数据丢掉,不可能出现上面大下面小的…...

使用docker安装zlmediakit服务(zlm)
zlmediakit安装 zlmediakit安装需要依赖环境和系统配置,所以采用docker的方式来安装不容易出错。 docker pull拉取镜像(最新) docker pull zlmediakit/zlmediakit:master然后先运行起来 sudo docker run -d -p 1935:1935 -p 80:80 -p 8554:554 -p 10000:10000 -p …...

SOLID原则-单一职责原则
转载请注明出处:https://blog.csdn.net/dmk877/article/details/143447010 作为一名资深程序员越来越感觉到基础知识的重要性,比如设计原则、设计模式、算法等,这些知识的长期积累会让你突破瓶颈实现质的飞跃。鉴于此我决定写一系列与此相关的博客&…...

Transformer究竟是什么?预训练又指什么?BERT
目录 Transformer究竟是什么? 预训练又指什么? BERT的影响力 Transformer究竟是什么? Transformer是一种基于自注意力机制(Self-Attention Mechanism)的神经网络架构,它最初是为解决机器翻译等序列到序列(Seq2Seq)任务而设计的。与传统的循环神经网络(RNN)或卷…...

Jdbc批处理功能和MybatisPlus
文章目录 1. 序言2. JDBC批处理功能和rewriteBatchedStatements3. JDBC批量插入的测试4. MybatisPlus#ServiceImpl.saveBatch()5. 结语:如果对大家有帮助,请点赞支持。如果有问题随时在评论中指出,感谢。 1. 序言 MybatisPlus的ServiceImpl类…...

对于相对速度的重新理解
狭义相对论速度合成公式如下, 现在让我们尝试用另一种方式把它推导出来。 我们先看速度的定义, 常规的速度合成方式如下, 如果我们用速度的倒数来理解速度, 原来的两个相对速度合成, 是因为假定了时间单位是一样的&am…...

Scala的属性访问权限(一)默认访问权限
//eg:银行账户存钱取钱 // 账户类: // -balance() 余额 // -deposit() 存钱 // -withdraw() 取钱 // -transfer(to:账户,amount:Dobule)转账 package Test1104 //银行账户class BankAccount(private var balance:Int){def showMoney():Unit {println(s"…...

【算法】(Python)贪心算法
贪心算法: 又称贪婪算法,greedy algorithm。贪心地追求局部最优解,即每一步当前状态下最优选择。试图通过各局部最优解达到最终全局最优解。但不从整体最优上考虑,不一定全局最优解。步骤:从初始状态拆分成一步一步的…...

条件logistic回归原理及案例分析
前面介绍的二元、多分类、有序Logistic回归都属于非条件Logistic回归,每个个案均是相互独立关系。在实际研究中,还有另外一种情况,即个案间存在配对关系,比如医学研究中配对设计的病例对照研究,此时违反了个案相互独立…...

redis7学习笔记
文章目录 1. 简介1.1 功能介绍1.1.1 分布式缓存1.1.2 内存存储和持久化(RDBAOF)1.1.3 高可用架构搭配1.1.4 缓存穿透、击穿、雪崩1.1.5 分布式锁1.1.6 队列 1.2 数据类型StringListHashSetZSetGEOHyperLogLogBitmapBitfieldStream 2. 命令2.1 通用命令copydeldumpexistsexpire …...

重学Android:自定义View基础(一)
前言 作为一名安卓开发,也被称为大前端,做一个美观的界面,是我们必备的基础技能,可能在开发中我们最常用的是系统自带的View,因为他能满足绝大部分需求,难一点的我们也可以上Github上找个三方库使用&#…...