学习笔记一:命名空间(namespace)之四:using declarations和using directives的区别

本文主要是介绍学习笔记一:命名空间(namespace)之四:using declarations和using directives的区别,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

(本文主要参考c++ primer第17章2.4节内容。)
由前面内容,我们知道c++引入命名空间(namespace)概念的主要目的是避免命名冲突。但是当我们想要引用命名空间成员时,我们就会体会到它相比直接使用变量或函数的不便之处,特别是当命名空间名字很长时,更是如此。比如,我们有一个函数printIsbn(),定义在命名空间cplusplus_primer内,我们引用该成员函数时,要这样写:

cplusplus_primer::printIsbn();

如果再复杂一点,命名空间嵌套命名空间(我们称之为”nested namespace”)的话,那么光写个函数名就得占一行。假如上面的cplusplus_primer是嵌套在命名空间primer下的一个命名空间,那么这个成员函数就变得更长:

primer::cplusplus_primer::printIsbn();

如果每次调用printIsbn()函数,都要写这么一大堆,我想每个人都会觉得烦。
当然,既然我们都能意识到这个问题,c++的设计者们当然也知道。所以针对这类问题,他们提出了三种解决方案:
(1)using declarations(using声明)
(2)namespace aliases (命名空间别名)
(3)using directives(using指示)
下面我们将会对这三种方法一一介绍。

(1)using declarations
什么是using declarations呢?
我们举个例子来说吧!
假设我们要在主函数中,输出”Hello World!”的字符串。代码如下:

#include <iostream>
int main()
{using std::cout;using std::endl;cout << "Hello World!" <<endl;return 0;
}

上述代码中的

using std::cout;
using std::endl;

的方式就是using declarations。
我们可以这样简单地理解using declarations方式:这种方式每次只引入一个命名空间成员,形式是 using namespace_name::member_name
使用using declarations方式的name的作用域:它服从一般的作用域法则。name,从using declarations开始一直到当前作用域结束,都是可见的。(用上面的例子来解释就是,cout这种缩写形式从using std::cout一直到main函数结束都是可用的。)而且它像普通变量和函数那样,如果在当前作用域中存在相同名称的实体,定义在外层的实体会被屏蔽。(using declarations似乎是声明命名空间成员名字的别名
举个小例子:

#include <iostream>
namespace A {int bi = 10;
}
int bi = 0;
int main()
{using std::cout;using std::endl;using A::bi;cout << "bi = "  << bi <<endl;return 0;
}

该段代码的输出结果为10。原因就是using A::bi中的变量bi的作用域是从using A::bi到main函数结束,它屏蔽了全局变量bi,所以输出结果为10 。
要点:using declaration可以出现在global,local,namespace内。类(class)中的using declaration只能使用在该类的基类中定义的命名空间成员。

using declarations,这种引入命名空间的方式有什么好处呢?
它能够让我们对自己程序中使用了哪些命名空间函数有很明确的认识和把握。并且由于它的局部性,使得它能最大可能地避免命名冲突。

(2)namespace aliases(给命名空间取别名)
对于一个名字过长的命名空间,我们也可以指定别名
假如我们自定义一个命名空间(命名空间不以分号结尾)

namespace cplusplus_primer { /* ... */ }

我们可以为这个命名空间指定别名:

namespace primer = cplusplus_primer;

(但是如果cplusplus_primer没有被定义的话,这句话就会报错)
(格式:namespace short_name = origin_name)
命名空间别名也可以用于nested namespace(即包含在命名空间下的命名空间)
比如
QueryLib是定义在命名空间cplusplus_primer内的一个命名空间,Query是它的一个类成员
当我们声明类Query的一个对象tq时,
一般会这样写

cplusplus_primer::QueryLib::Query tq;

如果用namespace aliases改写的话,我们可以为cplusplus_primer::QueryLib指定一个别名:

namespace QLib = cplusplus_primer::QueryLib;
QLib::Query tq;

一个命名空间可以有多个别名,而且别名和原始名之间可以交换使用。
小例子:

#include <iostream>
namespace std01 = std;
int main()
{std01::cout << "Hello World!" <<std::endl;return 0;
}

上面我们给std空间取别名为std01,在main函数中我们发现stdstd01都有用,std并没有因为取过别名之后而失去作用。

(3)using directives
using derective的形式是using namespace namespace_name;
//举例

using namespace std;

这种方式使得来自某个特定命名空间的所有命名在当前作用域都是可见的。
同样一个小例子:
(注意它和之前代码的区别)

#include <iostream>
int main()
{using namespace std;cout << "Hello World!" <<endl;return 0;
}

这里的using namespace std; 使得命名空间std下的所有命名在main函数下都可见。
注意:虽然using directives方式对于编程者而言,似乎使用起来更加方便(只要在全局变量中使用using namespace namespace_name,之后该命名空间下的所有命名都可以使用简写形式),但是我们最好不要那么做。因为当我们引用多个库时,采用using directives的方式,又会重新引起命名冲突问题,那么命名空间也就失去了它最初的作用。(文章开头提到:命名空间的目的就是避免命名冲突)

要点:using directive可以出现命名空间,函数和块中,但不能出现在类中。

<>内容由该篇博文http://blog.csdn.net/custa/article/details/5811160改写
<
primer原文:A using directive does not declare local aliases for the namespace member names. Rather, it has the effect of lifting the namespace members into the nearest scope that contains both the namespace itself and the using directive.
使用using directives方式的name的作用域:using directives不声明命名空间成员名字的别名,相反,using directives具有将命名空间成员提升到包含命名空间本身和 using 指示的最近作用域的效果。(只是效果,实际不是那么回事)
primer在解释作用域提升(也就是上面的lift)的时候,它用的是“as if”,只是看起来像而已,也就是说这并不等价于在“最近作用域”声明了命名空间成员。
两个小例子
Example 1(from Primer)

namespace blip {int bi = 16, bj = 15, bk = 23;// other declarations}int bj = 0; // ok: bj inside blip is hidden inside a namespacevoid manip(){// using directive - names in blip "added" to global scopeusing namespace blip;// clash between ::bj and blip::bj// detected only if bj is used++bi;           // sets blip::bi to 17++bj;           // error: ambiguous// global bj or blip::bj?++::bj;         // ok: sets global bj to 1++blip::bj;     // ok: sets blip::bj to 16int bk = 97;    // local bk hides blip::bk++bk;           // sets local bk to 98}

例子1,我们可以看到,从manip函数内部看,bj的作用域是全局。但通过例子2,我们从全局考虑,可以了解到这只是一种效果,并非真使得其成为一个全局变量。
Example 2(from blog http://blog.csdn.net/custa/article/details/5811160 )

#include <iostream>
using std::cout;
using std::endl;
namespace A  
{  int i = 1;  
}  namespace B  
{  using namespace A;  int i = 2;  void fn()  {  cout << i << endl;  }  
}  int main(int argc, char* argv[])  
{  B::fn();  
}

在B中使用using指示引入A中的成员,但这些成员看起来好像是在全局作用域(也就是包括命名空间本身和using指示的最近作用域)中声明的。
B中的fn()函数使用i并不会产生歧义,虽然使用using指示引入了A中的i,但是那看起来就像是在全局作用域里声明的,B本身声明的i屏蔽了外围作用域(全局作用域)中相同的名字。所以fn()调用的是B中的i,打印2。
还有这里强调了“好像是”(as if),它并不等价于在全局作用域中声明,如果等价于在全局作用域声明了A中的成员,那么可以在全局作用域定义这样的函数:

void fn_()  
{  cout << i << endl;  
}  

如果不是“好像”,而是“实际”时,它应该会打印输出2。而实际上在编译的时候会给出错误:i未声明。要访问命名空间A中的i,还是只能使用A::i访问。
>

注意:为了避免命名冲突,一般不要使用using directives,而使用using declarations。但有一种情况,using directives是有用的,那就是用在它自身的补充文件中(比如文中17.2.1提到的Sales_item.cc and Query.cc)

附:用命名空间成员理解作用域和生命周期
生命周期与作用域是两个不同的概念:生命周期是对象或变量生存的时段,作用域是对象或变量起作用的地方。那么我们如何形象地理解这两个概念呢?举个命名空间成员的小例子:

#include<iostream>
using std::cout;
using std::endl;
namespace blip {int bi = 0;
}
void fir_ip()
{using blip::bi;++bi;cout << "bi = " << bi << endl;
}
void sec_ip()
{using namespace blip;++bi;cout << "bi = " << bi << endl;
}
int main()
{using blip::bi;cout << "blip::bi = " << bi << endl;cout << "fir_ip" << endl;fir_ip();cout << "blip::bi = " << bi << endl;cout << "sec_ip" << endl;sec_ip();cout << "blip::bi = " << bi << endl;return 0;
}

运行结果为:
运算结果
从前面的内容,我们知道函数fir_ip() 中变量bi 作用域为using blip::bifir_ip() 函数结束;函数sec_ip() 中变量bi 为作用域为全局(效果上),而main()bi作用域也只是using blip::bimain() 结束。
如果认为生命周期和作用域是同一概念。那么我想输出结果应该为

blip::bi = 0
fir_ip
bi = 1
blip::bi = 0
sec_ip
bi = 1
blip::bi = 1

然而结果显然不是这样。我认为可以这样解释:

int bi = 0;

一开始就在命名空间blip (全局)中定义,所以blip::bi 生命周期是从定义blip::bi开始一直到程序结束。在fir_ip() 用“别名”bi 对它进行自增,虽然别名bi 的 作用域只在fir_ip() 内,但因为blip::bi 生命周期贯穿整个程序,blip::bi 通过别名bi 自增得到的值并不会随着fir_ip()bi的消亡而重置。

这篇关于学习笔记一:命名空间(namespace)之四:using declarations和using directives的区别的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/892018

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

变量与命名

引言         在前两个课时中,我们已经了解了 Python 程序的基本结构,学习了如何正确地使用缩进来组织代码,并且知道了注释的重要性。现在我们将进一步深入到 Python 编程的核心——变量与命名。变量是我们存储数据的主要方式,而合理的命名则有助于提高代码的可读性和可维护性。 变量的概念与使用         在 Python 中,变量是一种用来存储数据值的标识符。创建变量很简单,

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

native和static native区别

本文基于Hello JNI  如有疑惑,请看之前几篇文章。 native 与 static native java中 public native String helloJni();public native static String helloJniStatic();1212 JNI中 JNIEXPORT jstring JNICALL Java_com_test_g