C++名字空间

news/2024/10/9 19:14:11

基本概念

名字空间本质上是自定义作用域,由于C++设计的初衷是开发大规模软件,大量的软件库必然会加剧全局符号(变量、函数)的冲突,因此名字空间最基本的作用就是给不同的库和模块拥有自己的独特的作用域,处于不同名字空间中的重名符号相安无事,互不冲突,以此来大大提高编程的便利性。


1.1 定义与使用

定义一个名字空间,实际上就是定义一个作用域,在名字空间中可以定义变量、函数等,示例代码如下:

// ns.cpp
// 定义一个名字空间,叫ns
namespace ns
{// 在 ns 中定义变量int a = 1;// 在 ns 中定义函数float f(int x){return x/2;}
}

在以上名字空间 ns 中,定义了一个变量 a 和一个函数 f,但实际上它们的名字是 ns::a 和 ns::f,比如如下示例代码显示了如何正确地引用它们:

// ns.h
namespace ns
{// 对名字空间 ns 中的符号进行声明extern int a;extern float f(int x);
}
// main.cpp
#include "ns.h"int main()
{// 调用名字空间中的符号,使用全称cout << ns::a << endl;cout << ns::f(8) << endl;
}

此处出现了一个新的操作符 ==> :: ,其用法是:

名字空间::某符号
类::某符号

这个双冒号的操作符,称为作用域引用符,很显然,双冒号前面必须是一个作用域,在C++中,除了名字空间是作用域之外,后续会讲到的类也是最常见的作用域。

很显然,将全局变量 a 和函数 f() 放在名字空间中之后,可以极大避免由于不同程序文件或库的重名而引起的冲突。例如,在另外一个名字空间中,出现跟 ns 一样的变量或函数,它们一起使用相安无事:

// another_ns.cpp
namespace another_ns
{int a = 100;
}// ns.h
namespace ns
{extern int a;extern float f(int x);
}
namespace another_ns
{extern int a;
}// main.cpp
#include <iostream>
#include "ns.h"int main(void)
{cout << ns::a << endl;         // 输出1cout << another_ns::a << endl; // 输出100
}

另外值得注意的是,名字空间是对变量和函数的定义的作用域规定范围,因此是出现在源文件 *.cpp 中的,而对这些符号的声明,跟原来做法的一样 —— 在对应的头文件中进行声明,只不过在带有名字空间的场合中,头文件的声明语句也同样要包含在名字空间中。


1.2 using语句

在上述代码 main.cpp 中,使用了全称 ns::a 和 ns:f 来引用符号,在实际应用中很显然是很不方便的,有没有办法不需要重复写名字空间的名字 ns 也能使用里面定义的符号呢?答案是肯定的,只需要使用 using 语句即可,比如上述源码 main.cpp 可改成如下形式:

// main.cpp
#include "ns.h"
using namespace ns; // 导入名字空间:ns
int main()
{// 调用名字空间 ns 中的符号,不再需要写全名了cout << a << endl;cout << f(8) << endl;
}

对上述代码,需要强调的一点是,using 语句其实有两种形式:

// 形式一:导入整个名字空间中的所有符号
using namespace ns;// 形式二:导入名字空间中的指定符号
using ns::a;
using ns::f;

由于在上述例子中,名字空间 ns 仅仅包含极少量符号,因此不管采用哪种形式的 using 语句都没有什么区别,但如果某个名字空间包含大量符号(比如标准名字空间std),而程序中仅需用到其中的少量符号,那么导入整个名字空间的所有符号的做法也许是不明智的,因为这会使得大量未被使用的符号成为潜在的符号冲突候选人,这种情形被称为名字空间污染,因此,实际编码中我们应在追求便利的同时,尽量避免引入不使用的符号。


2. 进阶语法

2.1 内嵌名字空间

C++允许嵌套定义名字空间,即一个名字空间内部再出现另一个名字空间,这其实是作用域的常规特性,早在C语言时代就可以有嵌套的作用域的概念,只不过C语言中的作用域都是匿名的,而C++给这些作用域赋予了特定的名字。

// ns.cpp
namespace ns
{int a = 1; // 注意,此处a的全称是 ns::a// 在名字空间中嵌套另一个名字空间namespace nested_ns{int a = 2; // 注意,此处a的全称是 ns::nested_ns::aint x = 100;}
}

声明与使用:

// ns.h
namespace ns
{extern int a;namespace nested_ns{extern int a;extern int x;}
}// main.cpp
#include "ns.h"int main()
{cout << ns::a << endl;cout << ns::nested_ns::a << endl;cout << ns::nested_ns::x << endl;
}

2.2 扩展性
// main.cpp
#include <iostream>
#include "ns.h"namespace ns
{// 在名字空间 ns 中增添一个新的符号bint b = 666; 
}using namespace ns;int main(void)
{cout << a << endl; // 访问名字空间 ns 中原有的符号 acout << b << endl; // 访问名字空间 ns 中新增的符号 b
}

当程序在多处定义了相同的名字空间时,它们将会融合成一个统一的作用域。如:

// main.cpp
#include <iostream>
#include "ns.h"namespace ns
{// 在名字空间 ns 中增添一个新的符号bint b = 666; 
}using namespace ns;int main(void)
{cout << a << endl; // 访问名字空间 ns 中原有的符号 acout << b << endl; // 访问名字空间 ns 中新增的符号 b
}

2.3 全局作用域

全局作用域是从C语言就开始有的一种作用域,在C++中,有时为了强调某符号的全局特性,或为了避免与导入的名字空间中的重名符号冲突,会在使用全局符号的时候加上 作用域解析符

示例代码:

int global = 100;
int main()
{int global = 200;// 重名的标识符,外层的作用域会被内层的掩盖cout <<   global << endl; // 输出200// 使用双冒号引用全局作用域中的标识符cout << ::global << endl; // 输出100
}

全局作用域的名字空间是匿名的,引用全局作用域符号只需加 :: 即可。
名字空间的本质就是作用域,遵守C语言关于作用域的基本原则,如内层作用域重名符号会掩盖外层作用域的重名符号。


3. 小结

自定义名字空间,实际上是将原来C语言中的全局作用域做了更加细致的规划,在原先的全局作用域中,人为地将某个区域内的符号(变量、对象、函数)命个名圈起来,避免与全局作用域中的其余符号冲突。

如果不可避免地要引入不同的名字空间(比如space1和space2),并且不同的名字空间中恰巧有重名的符号(比如var),那么引用方只需将符号带上其所属的名字空间的名称即可(比如space1::var 和 space2::var)。这大大拓展了大型程序对不同函数库符号引入的灵活性,大大缓解了符号的冲突的可能。

引入名字空间的初衷,是解决大型软件中的各个不同产商提供的第三方库的名字冲突问题,这是从C语言到C++的一个重大的改变。

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

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

相关文章

『模拟赛』多校A层冲刺NOIP2024模拟赛04

『模拟赛记录』多校A层冲刺NOIP2024模拟赛04Rank 赤石场。A. 02表示法 签。 若干天前在洛谷随到过,不过当时只看了眼讨论区就走了www 还好本来不是很难。 发现大体上是一个拆分二的幂的问题,从大到小枚举 2 的幂,判断有没有这个幂只用比较大小关系,然后再对指数做一个同样的…

实现一个烟花效果

1. 首先创建一个烟花类,有烟花上升的效果,烟花爆炸的两种效果(爆炸型和球型)2. 创建线的属性方法,因为上升效果和爆炸效果都会用到3. 上升效果为了达到那种螺旋上升的效果可以通过sin函数实现一个点的偏移量4. 爆炸效果则是将随机生成多条半径不同的线5. 球形效果则是将规…

【Java】反射

Java中的反射机制 动态代理反射 允许对封装类的字段,方法和构造函数的信息进行编程访问 ==》 反射允许对成员变量,成员方法和构造方法的信息进行编程访问 基本操作:获取(获取class对象【字节码对象】) + 解剖 成员变量 Field —— 修饰符、名字、类型、赋值 构造方法 Cons…

DNShell

DNShell 一款基于DNS C2隧道的反弹shell工具。 支持 功能: 支持DNS-recordA-直连型 的C2隧道。 目标: Windows下基于Powershell的反弹。 Linux下基于ShellScript的反弹。 使用方法

【Redis】Redis学习笔记

概况 redis == remote Dictionary Server (远程字典服务) 基于内存的KV键值对内存数据库 作用:分布式缓存,与MySQL共同配合Redis -- 内存 MySQL -- 磁盘Redis -- NoSQL MySQL -- SQL内存存储 和 持久化(RDB+AOF)Redis支持一部将内存中的数据写入硬盘宕机 -- 可自行恢复高…

基于禁忌搜索算法的VRP问题求解matlab仿真,带GUI界面,可设置参数

1.程序功能描述基于禁忌搜索算法的VRP问题求解matlab仿真,带GUI界面,可设置参数。 2.测试软件版本以及运行结果展示MATLAB2022a版本运行 3.核心程序while COUNT<=Itertions ֲ L = zeros(Ant_Num,1); for i=1:Ant_Num Infor_Tabu_tmps = Infor_Tabu(i,:); R = Inf…

CMake 属性之全局属性

CMake 的全局属性是指在 CMake 配置过程中,对整个项目范围生效的设置。这些属性不同于目标 ( Target ) 属性或目录 ( Directory ) 属性,后者仅对特定的目标或目录生效。【写在前面】 CMake 的全局属性是指在 CMake 配置过程中,对整个项目范围生效的设置。 这些属性不同于目标…

自然人信息社工

人,是网络安全全流程中最大的弱点。针对人的攻击往往有出奇不意的效果。而想要利用人的弱点进行攻击,那么对目标的信息收集与了解就是非常重要的了。这篇文章记录了一些常用的用于对人进行身份信息收集的技术。这些技术常被用于溯源取证、社工攻击。 0x00 社工分析中的身份信…