【C++】命名冲突了怎么办?命名空间来解决你的烦恼!!!C++不同于C的命名方式——带你认识C++的命名空间

2024-05-03 13:04

本文主要是介绍【C++】命名冲突了怎么办?命名空间来解决你的烦恼!!!C++不同于C的命名方式——带你认识C++的命名空间,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

命名空间

  • 导读
  • 一、什么是C++?
  • 二、C++的发展
  • 三、命名空间
    • 3.1 C语言中的重名冲突
    • 3.2 什么是命名空间?
    • 3.3 命名空间的定义
    • 3.4 命名空间的使用环境
    • 3.5 '::'——作用域限定符
    • 3.6 命名空间的使用方法
      • 3.6.1 通过作用域限定符来指定作用域
      • 3.6.2 通过关键字using和关键字namespace来展开作用域
      • 3.6.3 通过关键字using和作用域限定符来展开指定作用域中的指定对象
  • 四、深挖命名空间
    • 4.1 命名空间中的内容
    • 4.2 命名空间的定义区域
    • 4.3 命名空间中对象的生命周期
      • 4.3.1 生命周期和作用域
      • 4.3.2 命名空间中对象的作用域
      • 4.3.3 命名空间中对象的生命周期
  • 结语

封面

导读

大家好,很高兴又和大家见面啦!!!

在正式开始咱们今天的内容前,我们先来看一下今年的编程语言排名:
编程语言排名
从排名中可以看到,目前位于前三的编程语言分别是Python、C、C++。在之前的学习中,我们已经学习了C的基础知识点,经过前一段时间的学习,也已经具备了基本的编程能力了。从今天开始,为了能够继续的提升自己的编程能力,我们将要开始学习排名第三的语言——C++了。

在学习一门新的计算机语言之前,我们首先要对这门语言有一个初步的了解,其次才是学习这门语言相关的知识点。那么在今天的内容中我们会简单的了解一下什么是C++以及C++的发展史,之后会重点学习C++的命名空间相关的知识点。下面我们就来开始进入C++的世界吧!

一、什么是C++?

C语言是结构化和模块化的语言,适合处理较小规模的程序。对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言则不合适。为了解决软件危机, 20世纪80年代, 计算机界提出了OOP(object oriented programming:面向对象)思想,支持面向对象的程序设计语言应运而生。
1982年,本贾尼·斯特劳斯特卢普(Bjarne Stroustrup)博士在C语言的基础上引入并扩充了面向对象的概念,发明了一种新的程序语言。为了表达该语言与C语言的渊源关系,命名为C++。因此:C++是基于C语言而产生的,它既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行面向对象的程序设计

看到这些概念,我是感觉大脑有点宕机,这里说的都是啥呀?又是过程又是对象的?真的是理解不了一点。

不过从上述信息中我们还是能提取一些信息的——C语言适合处理较小规模的程序,并不适合处理复杂的问题和需要高度抽象和建模的规模较大的程序。那是不是说明C++就是来解决C语言处理不了的问题的呢?

对于这个问题,我们不着急今天就把它弄懂,毕竟咱们才刚刚开始学习C++,我相信,随着咱们对C++的深入学习,我们会在学习的过程中慢慢的去理解C++这门语言的。

既然咱们已经踏入了C++的世界,那么咱们还是要认识一下咱们的祖师爷的,祖师爷帅照给大家奉上:

本贾尼
简单了解了一下什么是C++,下面我们就来看一看C++的发展史;

二、C++的发展

1979年,贝尔实验室的本贾尼等人试图分析 unix 内核的时候,试图将内核模块化,于是在C语言的基础上进行扩展,增加了类的机制,完成了一个可以运行的预处理程序,称之为C with classes。语言的发展就像是练功打怪升级一样,也是逐步递进,由浅入深的过程。我们先来看下C++的历史版本。

阶段内容
C with classes类及派生类、公有和私有成员、类的构造和析构、友元、内联函数、赋值运算符重载等
C++1.0添加虚函数概念,函数和运算符重载,引用、常量等
C++2.0更加完善支持面向对象,新增保护成员、多重继承、对象的初始化、抽象类、静态成员以及const成员函数
C++3.0进一步完善,引入模板,解决多重继承产生的二义性问题和相应构造和析构的处理
C++98C++标准第一个版本,绝大多数编译器都支持,得到了国际标准化组织(ISO)和美国标准化协会认可,以模板方式重写C++标准库,引入了STL(标准模板库)
C++03C++标准第二个版本,语言特性无大改变,主要:修订错误、减少多异性
C++05C++标准委员会发布了一份计数报告(Technical Report,TR1),正式更名C++0x,即:计划在本世纪第一个10年的某个时间发布
C++11增加了许多特性,使得C++更像一种新语言,比如:正则表达式、基于范围for循环、auto关键字、新容器、列表初始化、标准线程库等
C++14对C++11的扩展,主要是修复C++11中漏洞以及改进,比如:泛型的lambda表达式,auto的返回值类型推导,二进制字面常量等
C++17在C++11上做了一些小幅改进,增加了19个新特性,比如:static_assert()的文本信息可选,Fold表达式用于可变的模板,if和switch语句中的初始化器等
C++20自C++11以来最大的发行版,引入了许多新的特性,比如:模块(Modules)、协程(Coroutines)、范围(Ranges)、概念(Constraints)等重大特性,还有对已有特性的更新:比如Lambda支持模板、范围for支持初始化等
C++23制定ing

从C++的发展史的介绍中大家有发现什么亮点吗?1979年,贝尔实验室。这个时间这个地点,有没有是曾相识的感觉?

在继续后面的内容前咱们先来看一下C语言的发展史:

C语言的原型是A语言(ALGOL 60语言)。
1963年,剑桥大学将ALGOL 60语言发展成为CPL(Combined Programming Language)语言。
1967年,剑桥大学的Matin Richards 对CPL语言进行了简化,于是产生了BCPL语言。
1969年,美国贝尔实验室的Ken Thompson将BCPL进行了修改,提炼出它的精华,并为它起了一个有趣的名字“B语言”。并且他用B语言写了第一个UNIX操作系统。
而在1973年,美国贝尔实验室的D.M.RITCHIE在B语言的基础上最终设计出了一种新的语言,他取了BCPL的第二个字母作为这种语言的名字,这就是C语言。
为了使UNIX操作系统推广,1977年Dennis M.Ritchie 发表了不依赖于具体机器系统的C语言编译文本《可移植的C语言编译程序》。即著名的ANSI C。
1978年由AT&T(美国电话电报公司)贝尔实验室正式发表了C语言。同时Brian W.Kernighian和Dennis M.Ritchie出版了名著《The C Programming Language》一书。通常简称为《K&R》,也有人称之为《K&R》标准。但是,在《K&R》中并没有定义一个完整的标准C语言,后来由美国国家标准协会(American National Standards Institute,ANSI)在此基础上制定了一个C语言标准,于1983年发表。通常称之为ANSI C。从而使C语言成为目前世界上流行最广泛的高级程序设计语言。

大家从C语言的发展史中有没有发现什么?没错,同样是贝尔实验室,而且从1963年到1979年这段时间内,从贝尔实验室这个地方在这之前先后诞生了B语言、UNIX操作系统、C语言、C with classes。

世界上第一台通用计算机 ENIAC 于1946年问世,到1949年第一台离散自动变量电子计算机EDVAC问世,再到1969年B语言的诞生和计算机的第一个操作系统——UNIX操作系统的问世,再到1973年C语言的诞生,最后到1979年C with classes的诞生。在这53年间,计算机开始进入飞速发展的阶段。

而现在的互联网时代,计算机已经走向成熟,人们都开始向着人工智能的时代前进。在我们目前的日常生活中,最大的感受可能就是电子产品的迭代速度太快了,可能大家刚刚购买的新手机还没捂热,下一个版本的手机就发行了。不过话说回来,计算机能有现在的发展速度,这都离不开这些伟人所付出的时间、精力和心血。这里,我仅代表我自己像这些伟人致敬。

题外话咱们就先说到这里,总结一句话就是——C++从诞生到现在已经有了45年的发展历程了,其版本也经历了多次的迭代更新,最终才变成咱们现在接触的C++,并且它还在继续的向前发展。所以,计算机语言的学习是没有尽头的,咱们既然开始接触了这门新语言,就需要在平时花时间和精力去学习、理解与运用这门语言。

在简单的了解了C++以及它的发展历史之后,我们就要开始进入今天的重点内容——命名空间了。

三、命名空间

3.1 C语言中的重名冲突

通过前面的介绍,我们学习到了一件事——C语言存在不足,无法适应复杂的问题和需要进行高度抽象和建模的规模较大的程序。而C++则是用来处理这些问题的一门计算机语言,那在C语言中具体存在哪些不足呢?下面我们来看一个例子;

命名空间1

可以看到,现在我们在程序中平平无奇的进行了一个整型全局变量的定义,并对其值进行了输出,感觉上好像没什么问题,下面我们再来添加一个新的头文件再来看一下:

命名空间2

可以看到,当我们在添加了<stdlib.h>这个头文件之后,我们会发现,此时程序报出了一个错误——qsort重定义,重定义的原因是qsort之前是一个函数。对于qsort这个函数,我相信大家应该都不陌生了,它是用来给数组进行排序的一个库函数。

在C语言中,当我们在定义全局变量时如果与包含的头文件中的库函数重名的话,这时就会出现重定义的错误,这是我们与库重命时发生的冲突。我们自己也可能会有这样的冲突,如下所示:

命名空间3

当我们在多个文件中定义同名变量时,也会出现重定义的问题。这种情况常发生在一个大的工程中,需要多个人分工完成的情况,并且重定义的问题不仅仅是变量与变量、变量与函数之间,还有函数与函数、类型与类型之间。当出现这种情况时,改名则会是一种额外的非常大的工作量,相信大家都不愿意浪费时间在这个上面,这时就只能相互协调或者天台见。从这一点我们就能感受到,确实在大型的工程中,C语言不太适合。

为了改变这一现状,本贾尼祖师爷在C with classes中提出了命名空间的概念,命名空间也是从C with classes开始一直沿用至今。那什么是命名空间呢?

3.2 什么是命名空间?

在C语言中我们在定义变量时会有两个作用域——局部作用域和全局作用域,当在使用变量、函数、类型时,程序默认的查找顺序是:局部域->全局域。因此在C语言中是存在局部优先原则的,如下所示:

命名空间4
可以看到,当局部域的内容与全局域的内容重名时程序会优先选择局部域的内容,这就是局部优先原则。但是在下面这种情况下,还是会有重定义的冲突:

命名空间5

这是因为此时我们对外部变量的声明也出现在了同一个局部域中,这时就不存在局部优先原则了。

从上述的例子中我们可以看到能够避免重名冲突的方式是将重命的内容置于不同的作用域中。而本贾尼祖师爷提出的命名空间实际上是我们人为增加的一个新的作用域。也就是说,命名空间的本质是一个自定义作用域,我们也可以称其为命名空间域。

3.3 命名空间的定义

在C++中相比于C语言,它新增了很多的关键字,其中一个关键字就是namespace——命名空间。这个关键字就是专门用来定义命名空间域的。它的使用方式与自定义类型很像,结构如下所示:

//命名空间域的定义格式
namespace 自定义域名 {自定义内容;
}

那是不是说我们只要定义了一个命名空间域,我们就能对域中的内容进行使用了呢?下面我们就来探讨一下;

3.4 命名空间的使用环境

我们在使用命名空间前,首先肯定是需要通过关键字namespace定义一个命名空间,这里要注意的是,因为命名空间是C++中的内容,因此我们是无法在.c文件下正常使用的,如下所示:
命名空间6
因此我们只能在.cpp的文件下正常使用,如下所示:
命名空间7
可以看到,当我们在创建好.cpp的文件后再来使用命名空间,这时是没有任何问题的,因此我们一定要记住这个结论——命名空间只能在.cpp的环境下使用,无法在.c中使用。

在了解了命名空间的使用环境后,我们需要学习一个新的操作符——作用域限定符'::'

3.5 ‘::’——作用域限定符

在C语言中如果遇到局部变量与全局变量同名的问题时,此时的局部变量将会被完全屏蔽掉无法正常使用。C++中在引入命名空间后为了完善C语言的这一缺陷,同时还引入了一个新的操作符'::'——作用域限定符。这个操作符的使用如下所示:
作用域限定符

!!!声明
因为C++是由C衍生出来的一种计算机语言,因此C++能够兼容C的99%的语法,目前咱们还为学习C++的输入和输出,因此这里通过C的语法来进行测试。

作用域限定符的基本用法如上例中所示,在变量名的左边加上作用域限定符就可以指定全局域中的对象来进行操作。现在既然有了命名空间,如果我们想指定命名空间中的对象,是不是也可以采用这种方式的?为了弄清楚这个问题,接下来我们需要来探讨一下命名空间的使用方法;

3.6 命名空间的使用方法

命名空间在使用时有三种方式:

  • 通过作用域限定符来指定作用域;
  • 通过关键字using和关键字namespace来展开作用域
  • 通过关键字using和作用域限定符来展开指定作用域中的指定对象

接下来我们就来逐一介绍这三种使用方式;

3.6.1 通过作用域限定符来指定作用域

在前面的介绍中我们知道当我们在对变量、函数、类型……对象进行操作时,程序默认查找对象的顺序是:局部域->全局域。而当局部域和全局域中的对象重名时,我们可以通过作用域限定符来对二者进行区分。

现在我们也引入了命名空间,那此时程序的查找顺序会发生变化吗?下面我们就来测试一下:
命名空间9
从打印结果中我们可以看到,当全局域和命名空间域中的对象重名时,程序优先查找的是全局域,那是不是说明此时程序的默认查找顺序就变成了:局部域->全局域->命名空间域呢?

从这个例子中我们只能得到全局域是优先域命名空间域的,并不能得到程序的默认查找顺序,因此我们还需要继续测试:
命名空间10
在这次的测试中我们通过给命名空间ts加入了一个新的变量b,让后在测试函数中尝试着打印这个整型变量的值,结果发现失败了,失败的原因是——未定义的标识符’b’;

现在大家是不是会奇怪,我明明在命名空间中定义了,为什么无法使用呢?难道它需要作用域界限符?为了验证我们的这个猜想,下面我们继续测试:

命名空间11

从测试结果中可以看到,此时在加上作用域限定符后程序提示的错误是变量b不是全局域中的内容。现在我们就能得到两个结论:

  • 通过:: + 对象名的方式无法指定命名空间中的对象;
  • 程序无法直接操作命名空间中的对象;

那既然我们无法通过作用域限定符来使用命名空间中的对象,那前面为什么说可以通过作用域限定符来指定作用域呢?难道这里指的仅仅是全局域?

其实作用域限定符确实可以指定作用域,这里的作用域不仅是全局域,还有自定义作用域,只不过在刚才的测试中我们使用的方式错了。正确的使用作用域限定符来指定命名空间的格式如下所示:
命名空间名 + 作用域限定符 + 对象名 命名空间名 + 作用域限定符 + 对象名 命名空间名+作用域限定符+对象名
单看这个格式可能不太好理解,下面我们来看一个实例。还是刚刚的测试用例,只不过我们稍作修改,如下所示:

命名空间12
可以看到,当我们在作用域限定符的左侧加上指定的作用域的名字后,我们就能正常的访问指定的对象了。因此我们可以得到以下结论:

  • 当我们在使用变量、函数、类型……这些对象时,程序默认的查找顺序是:局部域->全局域。
    • 在未通过作用域限定符指明查找的作用域时,程序无法再已有的命名空间内进行查找;
    • 在通过作用域限定符指明查找的作用域后,程序才能在指定的作用域内进行查找;
  • 作用域限定符的使用格式为:
    • :: + 对象名——指定的作用域为全局域;
    • 作用域名 + :: + 对象名——指定的作用域为对应作用域名的命名空间

相信大家现在应该都学会了命名空间的第一种使用方法,下面我们继续往下看;

3.6.2 通过关键字using和关键字namespace来展开作用域

在介绍第二种使用方式前我们先来看一个例子:
命名空间13
可以看到,在这个例子中我们对命名空间中的函数func进行了多次的调用,但是每次都需要指定作用域名,那是不是有点麻烦呢?有没有什么方式能够帮助我们来简化代码呢?

这就是我们需要学习的命名空间的第二种使用方式:通过关键字using和关键字namespace来展开作用域。具体是什么意思呢?下面继续往下看:

命名空间14
从这次的测试中,我们看到此时程序是无法正常运行的,原因是因为对变量a的指定不够明确。在前面我们有得出一个结论,在未通过作用域名与作用域限定符来指定作用域时,程序不会在命名空间中查找对象。而此时我们通过using namespace将指定的作用域展开后,我们会发现,此时的func函数是能正常使用且不需要再通过作用域限定符来指定作用域了。这里可以给大家测试一下,我们此时将打印变量a的代码给屏蔽掉,再运行程序:
命名空间15
可以看到,此时程序是正常运行的。因此通过using namespace将命名空间展开后确实能够简化代码,但是不太建议大家使用这种方式。具体的原因就是因为前面的测试中在展开命名空间后对变量a进行打印时遇到的错误。

对于这个问题我们可以理解为,当我们将命名空间展开后,此时在全局域中就会出现两个同名的变量a,由于它们的出处不同——一个来自全局域,一个来自命名空间域,因此并不构成重定义的问题。但是此时的全局域中存在两个为a的变量,如果我们在对变量a进行使用时,没有指明它的所属,那么就会导致程序无法判断应该使用哪个变量a,这就是为什么系统会提示a不明确。当然解决的办法就是指明对象,如下所示:

命名空间16
可以看到,此时在指明了a的作用域后,程序又能正常运行了。因此为了减少不必要的麻烦,建议大家还是不要通过using namespace来展开命名空间,对使用的对象,该指明作用域时还是需要指明对象所在作用域的,不要偷懒哦!

这时可能就会有朋友问了,既然不建议使用这种展开方式,那有没有其它方式能简化代码呢?答案是有的,我们继续往下看;

3.6.3 通过关键字using和作用域限定符来展开指定作用域中的指定对象

在C++中通过using namespace将命名空间展开的方式称为完全展开,意思就是不管命名空间中存在什么内容,都会将其展开到全局域中,这样就会导致前面咱们遇到的问题——当两个作用域中存在同名对象时,如果不指定对应的作用域,则无法正常运行程序。

因此对于不同作用域中存在的同名对象而言,不管我是否将其展开,我都要指明其所在作用域,那还不如我在展开时,只展开我需要的部分。这就是我们要介绍的第三种方式——通过关键字using和作用域限定符来展开指定作用域中的指定对象。具体使用方式如下所示:

命名空间17
可以看到,当我们通过这种方式将指定的函数func展开后,在之后的使用中我们同样不再需要对其进行指定作用域了。这样既能达到简化代码的效果,又能减少使用对象不明确的问题;

经过前面的介绍,相信大家对命名空间及其使用有了一个基本的认知了,下面我们就来继续深挖一下命名空间。

四、深挖命名空间

现在我们会从三个方面来对命名空间进行进一步的解析:

  • 命名空间中的内容;
  • 命名空间定义的区域;
  • 命名空间中对象的生命周期;

可能看这三个内容,大家还无法理解,下面就跟着我的思路一起往下看;

4.1 命名空间中的内容

在前面的介绍中,我们在命名空间中定义了变量、函数。现在我们要深挖的第一个内容就是在命名空间中我们还能定义哪些内容?如下所示:

深挖命名空间1
在这次测试中,我们在命名空间ts_space1中定义了变量、数组、指针、函数、结构体类型、以及命名空间。在将命名空间ts_space1完成展开后,我们对这些内容进行了逐一的访问,从测试结果我们可以看到,我们在命名空间中定义的所有内容都是能够正常访问的,因此我们可以得到结论:

  • 命名空间作为一个自定义的作用域,它不仅能像局部域和全局域一样定义变量、数组、指针、函数、自定义类型,它还能在作用域中嵌套定义新的自定义作用域。

在清楚了命名空间中可定义的内容后,我们接着来挖掘一下命名空间可以定义的区域;

4.2 命名空间的定义区域

在前面的定义中,我们都是在全局域中定义的命名空间,接下来我们主要测试的是我们能否在局部域中来定义命名空间,如下所示:
深挖命名空间2
从这次测试结果中我们可以看到我们在局部域中进行命名空间的定义时,此时程序无法正常执行,出现的错误是——不允许进行命名空间定义,并且提示我们命名空间的定义必须出现在文件范围内或者出现在另一个命名空间定义内。

在明确了命名空间具体的定义区域后,我们再来深挖一下命名空间中对象的生命周期;

4.3 命名空间中对象的生命周期

这里为了简单一点,我们就拿整型变量为例,来进一步探讨命名空间中对象的生命周期。开始探讨前我们需要先对生命周期和作用域这个知识点做个简单的回顾;

4.3.1 生命周期和作用域

  • 全局变量与局部变量的生命周期
    • 全局变量的生命周期是伴随整个工程的。在创建好一个全局变量后,该变量随着工程的结束而销毁;
    • 局部变量的生命周期是伴随对应的局部域的。在创建好一个局部变量后,该变量随着局部域的结束而销毁;
  • 全局变量与局部变量的作用域
    • 全局变量的作用域是伴随着对应项目的。当我在一个项目中创建好一个全局变量后,我可以直接在该项目中进行使用,而在其它项目中使用时需要通过关键字extern来对其进行声明;
    • 局部变量的作用域是伴随对应的局部域的。在创建好一个局部变量后,我只能在局部变量所在的局部域中正常使用,当出了该局部域,变量就销毁了。

我知道,如果仅仅是通过文字来进行复习,大家可能没有什么感觉,下面我们通过例子来分别说明全局变量与局部变量的生命周期与作用域:

命名空间中对象的生命周期1

在这次的测试中我们在原先的项目test3.cpp中创建了一个全局变量m、一个函数func2、和一个函数test5,然后新建了一个项目test4.cpp,并在该项目中创建了一个全局变量y,然后在test3.cpp这个项目中通过extern进行了声明。在func2函数的局部域中我们创建了一个局部变量z并在函数中对这三个变量进行了打印,然后在test5这个函数中又调用了func2这个函数,调用完后又对这三个变量进行了打印;

以上就是我们在这次测试中所做的全部内容,在开始运行程序后我们发现此时程序无法正常运行,报错原因是在112行,也就是test5这个函数中对局部变量z的访问。之所以只在这里报错,是因为前面的程序都是能够正常运行的,只有这一句代码无法正常执行。下面我们把这个代码屏蔽后再来运行一下,看是什么结果:
命名空间中对象的生命周期2

这一次的结果中我们可以看到,对于该项目中的全局变量m因为我们并未赋予初始值,所以全局变量自动初始化为0;对于另一个项目中的全局变量y我们在创建时对其初始化为20,并通过extern这个关键字进行了全局变量的声明,因此它的结果就是我们初始化的结果;而对于局部变量z,当我们将test5函数中对z进行访问的代码屏蔽后,此时程序正常运行,并在func2函数中成功进行了访问。

接下来我们在把对全局变量y的声明给屏蔽掉,然后再来看一下测试结果:
命名空间中对象的生命周期3
可以看到此时程序又无法正常运行了,这次的问题是y是未声明的标识符。这个例子就能很好的说明全局变量与局部变量的作用域和生命周期:

  • 作用域
    • 对于全局变量m而言,它的作用域是它所在的项目test3.cpp,所以我们能在该项目中正常使用;
    • 对于全局变量y而言,它的作用域是它所在的项目test4.cpp,所以我们在test3.cpp这个项目中进行使用时需要通过extern来进行声明,声明后,它也能在该项目中正常使用;
    • 对于局部变量z而言,它的作用域就是它所在的局部域,也就是func2函数中,因此我们可以在作用域内使用该变量,而在test5这个函数的局部域中无法使用该变量;
  • 生命周期
    • 对于全局变量m和y而言,它们的生命周期是整个工程,因此当我们在跨项目使用时,只要声明了全局变量就能正常使用,在整个工程没有结束前,全局变量都不会被销毁;
    • 对于局部变量z而言,它的生命周期是所在的func2函数的局部域中,当在该局部域中时,它可以正常使用,在其它局部域中无法使用的原因就是它在出了对应局部域后就销毁了。

现在大家应该就能很好的理解生命周期和作用域了,总结一下就是:

  • 作用域是指对象能够直接使用的区域。全局变量的作用域是对应的项目,局部变量的作用与是对应的局部域;
  • 生命周期是指对象存在的区域。全局变量的生命周期是整个工程,局部变量的生命周期是对应的局部域;

4.3.2 命名空间中对象的作用域

经过刚才的复习,相信大家已经理解什么是作用域了。通过前面对命名空间使用方法的介绍,我们知道了对于命名空间中对象来说,当我们需要使用它们时,我们必须声明它所在的作用域,也就是说命名空间中对象的作用域就是在对应的命名空间域内,如下所示:

命名空间中对象的生命周期4
可以看到此时我们在声明后是可以对其进行使用的,如果未对变量所属的作用域进行声明,那么则无法正常使用该变量,如下所示:

命名空间中对象的生命周期5
这个是前面有提到过的内容,这里我就不再多赘述,我们直接说结论:

  • 命名空间中对象的作用域就是该对象所在的命名空间,当我们将该命名空间展开后,命名空间中的对象会类似全局域中的对象,我们可以在该项目中任意使用命名空间中的对象,未展开命名空间时,则需要在使用时对其进行声明;

4.3.3 命名空间中对象的生命周期

通过前面的复习,我们知道全局变量的生命周期是伴随整个工程的,而局部变量的生命周期是伴随对应作用域的。对于命名空间而言,它是在全局域中定义的一个新的作用域,而位于命名空间中的对象会像在全局域中一样可以在所在项目中任意使用,那是不是说明它们也和全局域中的对象一样,生命周期是跟随整个工程的呢?

我们先来看一下是否能在不同项目中定义同名的命名空间:
命名空间中对象的生命周期6
从测试结果我们可以看到,完全没问题,那下面我们接着测试它们是否为同一个作用域:

命名空间中对象的生命周期7

从测试结果可以看到,虽然它们同名,但确实属于不同的命名空间,那是不是说我也可以在该空间中定义一个同名的变量w呢?继续测试:

命名空间中对象的生命周期8
从这次的测试结果中我们可以看到,此时系统提示的是w变量重定义。这个结果就说明了我们在test4.cpp中定义的变量w并未被销毁,而是一种存在于整个工程中的。也就是说命名空间中对象的生命周期和全局变量一样也是伴随整个工程的。

结语

在今天的内容中我们简单了解了一下什么是C++以及C++的发展历程,简单的理解就是:

  • C++是用来处理C语言处理不了的问题的一门高级计算机语言,从诞生至今已经发展了45年了。

之后我们详细介绍了C++中引入的新的概念——命名空间,以及命名空间的使用,并深入探讨了命名空间的三个点——命名空间中的内容、命名空间的定义区域以及命名空间中对象的生命周期。总结一下就是:

  • 命名空间的本质是一个自定义的作用域,与全局域相似,但不等于全局域;
  • 我们可以通过关键字namespace来定义一个命名空间;
  • 我们有三种命名空间的使用方式:
    • 指定命名空间——命名空间名 :: 对象名
    • 全展开命名空间——using namespace 命名空间名
    • 局部展开命名空间——using 命名空间名 :: 对象名
  • 命名空间域与全局域和局部域一样,可以在该作用域中定义变量、函数、数组、指针、自定义类型甚至是命名空间域;
  • 命名空间只能定义在全局域中;
  • 对于同一个对象而言,它在局部域、全局域和命名空间域中的作用域与生命周期为:
    • 当对象位于局部域时,它的作用域和生命周期都是跟随所在局部域;
    • 当对象位于全局域时,它的作用域是跟随所在项目,而它的生命周期是跟随整个工程;
    • 当对象位于命名空间域时,它的作用域是跟随所在项目,而它的生命周期是跟随整个工程;

到这里咱们今天的内容就全部结束了,希望通过今天的内容,大家能够对C++这门高级计算机语言有一个初步的认识,并且能够了解这门语言中的新概念——命名空间。如果大家喜欢博主的内容,可以给博主来一波点赞、收藏加评论三连支持一下,当然也可以转发给你身边有需要的朋友。最后感谢各位的支持,咱们下一篇再见!!!

这篇关于【C++】命名冲突了怎么办?命名空间来解决你的烦恼!!!C++不同于C的命名方式——带你认识C++的命名空间的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

每天认识几个maven依赖(ActiveMQ+activemq-jaxb+activesoap+activespace+adarwin)

八、ActiveMQ 1、是什么? ActiveMQ 是一个开源的消息中间件(Message Broker),由 Apache 软件基金会开发和维护。它实现了 Java 消息服务(Java Message Service, JMS)规范,并支持多种消息传递协议,包括 AMQP、MQTT 和 OpenWire 等。 2、有什么用? 可靠性:ActiveMQ 提供了消息持久性和事务支持,确保消

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

变量与命名

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

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_