目录

🍯1.1 命名空间
🥝1. 命名空间的概念
在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突,例如我们下图中定义的int型变量名称 rand 与库函数中的函数名相同,发生了冲突: ![]()
使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中,namespace关键字的出现就是针对这种问题的;同样是上面的例子,我们可以通过使用namespace关键字将int型变量rand的定义括起来,使其与全局作用域的空间隔绝开,就不会发生冲突了,代码如下:
#include #include //命名冲突 namespace kiko //有一点点像结构体,对外面空间进行隔离 { int rand = 0; } int main() { printf("%p\n", rand); //rand此时打印的是函数指针,rand是库函数里的函数名 return 0; }
值得注意的是,此时由于我们将int型的rand使用命名空间隔离开了,因此我们打印的是库函数
中的 rand函数的指针地址,此时我们如何打印kiko这个命名空间里的rand呢?这里我先简单透露一种方法,就是使用作用域运算符(::)来指定不同作用域的rand。
#include #include //命名冲突 namespace kiko { int rand = 0; } int main() { printf("%d\n", kiko::rand); //打印kiko这个命名域中的rand(整型rand) printf("%p\n", ::rand); //打印全局域中的rand(函数rand) return 0; }
🥝2. 命名空间的定义
定义命名空间,需要使用到 namespace 关键字,后面跟命名空间的名字,然后接一对{ }即可,{ }中即为命名空间的成员,其一般代码格式如下:
namespace 命名空间的名字 { ··· //命名空间中可以定义变量、函数、类型 }1、正常定义的命名空间
比如我们想要定义一个命名空间为kiko_place,在这个命名空间里包含整型变量、加法函数和结构体类型,那么我们就可以这样定义:
namespace kiko_place { int age=18; //定义变量 int Add(int x, int y) //定义函数 { return x + y; } struct ListNode //定义结构体类型 { struct ListNode* next; int val; }; }2、嵌套定义的命名空间
我们可以在命名空间再嵌套命名空间,比如我们希望在kiko_place这个命名空间中,再设置一个名为求两数平均值的Add函数,我们发现这个新设置的Add函数会和原本kiko_place中定义的Add加法函数重名,进而导致命名冲突;此时我们就可以在kiko_place中再定义个子命名空间,将该空间与kiko_place的命名空间再进行隔离。
namespace kiko_place { int Add(int x, int y) //定义加法函数 { return x + y; } namespace kiko_son //定义一个子命名空间kiko_son { int Add(int x, int y) //定义平均值函数 { return (x+y)/2; } } } int main() { //输出kiko_place这个命名空间中的Add函数 printf("%d\n", kiko_place::Add(20, 30)); //输出kiko_place里kiko_son这个命名空间中的Add函数 printf("%d\n", kiko_place::kiko_son::Add(20, 30)); return 0; }
3、多个名称相同的命名空间
同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成到同一个命名空间中。例如我们在num1.h与num2.h中分别定义一个名为kiko_place的命名空间,最后检验一下在两个不同文件中定义的同一名称的命名空间是否合并。
//num1.h中写: namespace kiko_place { int age = 18; //定义变量 } //num2.h中写: namespace kiko_place { int Add(int x, int y) //定义函数 { return x + y; } } //test.cpp中写: #include"num1.h" #include"num2.h" #include int main() { printf("%d\n", kiko_place::age); printf("%d\n", kiko_place::Add(20,30)); }
🥝3. 命名空间的使用
命名空间主要有以下两种种使用方式:(1)使用作用域操作符、(2)使用using声明。
Q1:什么是作用限定符?
A1:作用域操作符就是“ :: ”,例如上面的例子中的 kiko_place::age,它的含义就是从操作符左侧名字所示的作用域中寻找右侧的那个名字,即在kiko_place这个作用域中寻找一个名为age的东东,这个东东可能为变量、结构体或者函数。
namespace kiko_place { int Add(int x, int y) //定义加法函数 { return x + y; } namespace kiko_son { int Add(int x, int y) //定义平均值函数 { return (x+y)/2; } } }为了方便接下来的命名空间的讲解,我们将共同使用上述的这一段代码,分别讲解使用命名空间的两种方法。
1、命名空间名称+作用限定符
int main() { //找kiko_place中的Add函数 printf("%d\n", kiko_place::Add(20,30)); //找kiko_place中的kiko_son中的Add函数 printf("%d\n", kiko_place::kiko_son::Add(20,30)); = }2、使用using声明
当我们想要频繁使用Add函数时,需要不停地在Add函数前添加前缀“xxx::”,这样的操作较为麻烦,此时我们就可以使用using声明,using声明可以使得我们直接访问命名空间中的名字,其一般格式如下:
using namespace 指定的作用域名;因此这时当我们想要使用kiko_place这个命名空间中的函数时,就可以在使用前先添加一句using声明。但是值得注意的是,使用了kiko_place这个声明只能帮助省略kiko_place::这个前缀,无法省略后续的限定,例如代码kiko_place::kiko_son::Add(20,30),在使用完using kiko_place后,会变为kiko_son::Add(20,30),只省去了前缀,具体代码如下:
using namespace kiko_place; //此处指定的作用域为kiko_place int main() { printf("%d\n", Add(20,30)); //这时使用kiko_place中的Add就不需要添加前缀了 printf("%d\n", kiko_place::Add(20,30)); //当然继续添加也是没有问题的 //using kiko_place只能省去kiko_place这个前缀,后面的限定不能省略 printf("%d\n", kiko_son::Add(20,30)); }
当然我们也可以using一个命名空间的子空间,我们只需要记住using后面必须跟一个作用域名,不管这个作用域是一个总命名空间,还是一个命名空间的子空间。
using namespace kiko_place::kiko_son; //此处指定的作用域为kiko_place::kiko_son int main() { printf("%d\n", Add(20,30)); //这时直接输出的Add函数就属于kiko_son这个子命名空间 }