【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]的数据丢掉,不可能出现上面大下面小的…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
AGain DB和倍数增益的关系
我在设置一款索尼CMOS芯片时,Again增益0db变化为6DB,画面的变化只有2倍DN的增益,比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析: 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...
