
🔥小叶-duck: 个人主页
❄️个人专栏:《Data-Structure-Learning》
✨未择之路,不须回头
已择之路,纵是荆棘遍野,亦作花海遨游
目录
[2.1 ::(域作用限定符):访问域中成员](#2.1 ::(域作用限定符):访问域中成员)
前言
在上一篇文章我们讲解完计数排序:高效非比较排序解析后初阶数据结构的知识点我们也就全部讲解完了。从这篇文章开始我们就正是开始学习C++语言的知识了,C++的知识非常的多且相较于前面学习的不管是C语言还是数据结构难度都还要大,C++的学习是一段非常漫长的持久战,但是我们选择了这条路就必须走到底!
一、C++的第一个程序
C++兼容C语言 绝大多数的语法,所以C语言实现的 hello world 依旧可以运行,C++中需要把定义文件代码后缀 改为**.cpp**,vs编译器看到是.cpp就会调用C++编译器编译,linux下要用g++编译,不再是gcc
cpp
// test.cpp
#include<stdio.h>
int main()
{
printf("hello world\n");
return 0;
}

但是既然我们学习的是C++语言,再用C语言那些代码写肯定没意义,所以C++有一套自己的输入输出,严格说C++版本的 hello world 应该是这样写的:
cpp
#include<iostream>
using namespace std;
int main()
{
cout << "hello world" << endl;
return 0;
}
第一次看到这些以前从来没见过的东西很多人可能就是眼前一黑,说namespace、std、cout等等这些都是啥啊,完全看不懂,感觉C++我学不清楚这些想法也会产生。这是非常正常的,在我们第一次接触编程的时候我们也是这样的想法,但是只要我们认真学,自然就不会有这些想法了。接下来我就会一一讲解这些名词是什么意思以及怎么去运用。
二、命名空间
1、namespace的价值
c语言项目类似下面程序这样的命名冲突是普遍存在的问题:
cpp
#include<stdio.h>
#include<stdlib.h>
int rand = 10;
int main()
{
printf("%d\n", rand);
return 0;
}

之所以会出现这个情况,原因就是在C语言学习中我们知道在预处理阶段 ,会把stdlib.h 头文件的所有内容全部拷贝到该文件中,而在这个头文件里面已经存在了 rand 这个函数,所以再用这个名字来命名变量就会导致这个结果。
在C/C++中,变量、函数和后面要学到的类都是大量存在 的,这些变量、函数和类的名称将都存在于全局作用域 中,可能会导致很多冲突 。使用命名空间 的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,C++ 引入 namespace 就是为了更好的解决这样的问题。
2、namespace的定义
定义命名空间,需要使用到 namespace 关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。命名空间中可以定义变量/函数/类型等。
cpp
// A 是命名空间的名字,一般开发中是用项目名字做命名空间名。
namespace A
{
//命名空间中可以定义变量/函数/类型
int rand = 10;
int Add(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
}
2.1 ::(域作用限定符):访问域中成员
在C语言学习中我们知道一个变量如果在全局进行了定义,在局部也进行了定义,则会就近原则 在局部找变量:
cpp
#include<stdio.h>
int rand = 20;
int main()
{
int rand = 30;
printf("%d\n", rand);
return 0;
}

那我们怎么样才能跳过局部去访问全局变量呢?就是需要用到**::** ,这个叫做域作用限定符 ,顾名思义就是能限定在一个域中进行访问 ,而如果是要访问全局 的话则左边不需要加任何东西:
cpp
#include<stdio.h>
//#include<stdlib.h>
int rand = 20;
int main()
{
int rand = 30;
printf("%d\n", ::rand);
return 0;
}

namespace 本质 是定义出一个域 ,这个域跟全局域各自独立 ,不同的域可以定义同名变量,所以就不会和下面的rand冲突了。
由于如果单纯访问一个变量 ,只会在局部或者全局中查找,如果没有则访问失败,而 namespace 这个域又是不同与这两个域的另一种域,这样的话我们如果想要访问到 namespace 里面的成员,就需要借助 :: 了
cpp
#include<stdio.h>
//域
// A 是命名空间的名字,一般开发中是用项目名字做命名空间名。
namespace A
{
//命名空间中可以定义变量/函数/类型
int rand = 10;
int Add(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
}
int rand = 20;
int main()
{
int rand = 30;
printf("%d\n", rand);
printf("%d\n", ::rand);
printf("%d\n", A::rand);
return 0;
}

C++中域有函数局部域 ,全局域 ,命名空间域 ,类域 。域影响的是编译时语法查找一个变量/函数/类型出处(声明或定义)的逻辑,所有有了域隔离,名字冲突就解决了。局部域和全局域除了会影响编译查找逻辑,还会影响变量的生命周期 ,命名空间域和类域不影响变量生命周期。
生命周期在前面学习当中我们已经提及,意思就是一个变量从创建到被销毁期间的时间。局部域中的变量当出了域就会被操作系统自动销毁,而全局域的变量是当程序运行终止时被销毁;但是命名空间域和类域不会影响变量生命周期,相当于可以把这两个域中创建的变量当成全局变量 ,但是必须利用 ::(域作用限定符)才能进行访问。
namespace 只能定义在全局 ,当然他还可以嵌套定义。
前面一句话意思就是除了在全局中能使用 namespace 这个域,像在函数中是无法定义 namespace 的。
在前面C语言学习中我们知道函数 这个东西是可以嵌套调用 的,但是不允许嵌套定义 ,namespace 不同于函数就是可以进行嵌套定义 ,这个一般是在用在写大型项目 时需要进行分配工作,这些分开进行的叫做项目组 ,而一个项目组是用一个命名空间 的,但一般而言一个项目组也需要有两三个人完成,这可能就会导致几个人所写代码会有冲突的情况,如果直接全部放在一个命名空间中就一定会出现问题,所以这个时候嵌套定义命名空间就有作用了,不同的域就会避免冲突的情况。
cpp
#include<stdio.h>
namespace A
{
//命名空间中可以定义变量/函数/类型
namespace B //命名空间的嵌套
{
int rand = 10;
int Add(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
}
namespace C
{
int rand = 40;
int Add(int left, int right)
{
return (left + right) * 10;
}
}
}
int rand = 20;
int main()
{
int rand = 30;
printf("%d\n", A::B::rand);
printf("%d\n", A::C::Add(1, 1));
return 0;
}

项目工程中多文件 中定义的同名 namespace 会认为是一个 namespace ,不会冲突。
C++标准库都放在一个叫std(standard)的命名空间中。
3、命名空间的使用
编译查找一个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间里面去查找。所以我们如果要使用命名空间中定义的变量/函数,有以下三种方式:
指定命名空间访问,项目中推荐这种方式。
在上面讲解 :: 这个域作用限定符我们已经实现了,这里就不重复讲解了。
using将命名空间中某个成员展开,项目中经常访问的不存在冲突的成员推荐这种方式。
而 using 又是一个我们在前面从来没有接触的关键词,这个关键词的作用就是将命名空间中的某个成员或者整个命名空间进行展开 ,以便后续可以不需要借助域作用限定符::来实现访问成员。
但是 using 这个关键词所理解的展开并不是之前包含头文件那种理解,包含头文件是在程序预处理阶段将头文件所有内容全部拷贝一份到文件中;而 using 的展开是使得编译器在查找时除开局部和全局还会考虑 using 所展开的内容。
cpp
#include<stdio.h>
namespace A
{
//命名空间中可以定义变量/函数/类型
namespace B //命名空间的嵌套
{
int rand = 10;
int Add(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
}
namespace C
{
int rand = 40;
int Add(int left, int right)
{
return (left + right) * 10;
}
}
}
int rand = 20;
using A::C::rand; //使得编译器查找该变量时会考虑到命名空间C中的这个变量
int main()
{
int rand = 30;
printf("%d\n", A::B::rand);
printf("%d\n", A::C::Add(1, 1));
return 0;
}

展开命名空间中全部成员,项目不推荐,冲突风险很大,日常学习以及练习代码为了方便推荐使用。
因为项目中命名空间内容非常大,如果直接使用 using 进行展开编译器在查找时就会全部考虑在内,很可能导致前面所说的代码冲突。
cpp
#include<stdio.h>
namespace A
{
//命名空间中可以定义变量/函数/类型
namespace B //命名空间的嵌套
{
int rand = 10;
int Add(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
}
namespace C
{
int rand = 40;
int Add(int left, int right)
{
return (left + right) * 10;
}
}
}
using namespace A; //使得编译器查找时会考虑到命名空间A中的所有内容
int main()
{
int rand = 30;
printf("%d\n", B::rand);
printf("%d\n", C::Add(1, 1));
return 0;
}

结束语
到此,C++入门基础中的命名空间namespace就讲解完了,后面还有许多在之前的学习中完全没有见过的东西,后续的学习我会为大家一一讲解清楚。希望这篇文章对大家学习C++有所帮助!
C++参考文档:
https://legacy.cplusplus.com/reference/
https://zh.cppreference.com/w/cpp
https://en.cppreference.com/w/