文章目录
命名空间概念
在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存 在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化, 以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。
很多人在开始接触C++的时候,知道打印一行 hello world
都是这样写的:
#include
using namespace std;
int main()
{
cout << “hello world” << endl;
}
但是要问你iostream 和 namespace std 是什么,在这行代码中起到什么作用,很多初学者基本上是回答不上来的,就知道怎么写,不知道其中的意义。
那么现在就开始认识命名空间这个概念
大家都知道C++是在c语言基础上建立的,c语言很多地方存在很多缺陷的,C++就创建了一个命名空间的概念来弥补这一缺陷
这一缺陷是什么呢?创建一个全局变量 int rand = 0 ,你只引入了头文件 <stdio.h> ,利用printf打印出rand,不会产生任何问题。可当你再引用了 <stdlib.h> ,再次运行程序就会出现问题
这里报错,显示rand重定义,这里的问题叫做命名冲突,因为 stdlib.h 这个库里面已经定义了rand,且定义成的一个函数,而我们这个全局变量rand是int类型的元素,系统在识别这个rand的时候就会找到两个rand,它不知道去打印哪一个,当然这里刚好rand在库里面是个函数 且与%d发生冲突,显示两个错误
在c语言中,解决这一问题,最简单粗暴的方法,就是改变量名称,防止与库里面的同名变量命名冲突,这样当然可以解决问题,但如果你在一个团队中需要分组完成一个项目,每个组的个别变量都一样,这时候怎么办,总不能组与组直接吵一架吧,这会大大降低工作效率不说,也影响团队氛围嘛,所以显然这种方法不可取。
既然c语言不能解决这个问题,那么C++就提出了命名空间
来解决
这里就要引入一个关键字 namespace
,它的作用是创建一个域把创建的rand变量保护起来,下面是代码实现
namespace Yuan
{
int rand = 0;
}
int main()
{
printf("%d\n", rand);
}
namespace
后面的名字随便取什么都行,只要自己记得住
这样就不会产生命名冲突的问题,main里面打印的是库函数里面的rand(准确来说是访问全局的域来找到rand),并不是Yuan里面的rand
要想打印Yuan里面的rand需要在rand前面加Yuan::
namespace Yuan
{
int rand = 0;
}
int main()
{
printf("%d\n", Yuan::rand);
}
namespace
创建的是一个命名为Yuan的局部域,这里需要回顾一下C语言里面的全局变量和局部变量
int i = 0;
int main()
{
int i = 1;
printf("%d", i);
}
现在问大家一个问题,这里打印的i是0还是1?
答案是1,这里满足一个
就近原则
,打印结果是1,这里是直接访问的局部域里面的局部变量i
但是如果你想在局部域里面访问全局变量i,怎么办?
需要在i前面加
::
int i = 0;
int main()
{
int i = 1;
printf("%d\n", ::i);
printf("%d\n", i);
}
::
叫做域作用符,作用是指定在哪个域里面去找这个i,这里::
左边是空白,就代表在全局域里面去找
所以命名冲突问题有了命名空间这个概念,就迎刃而解了。A B C三个小组,每个小组写的所有的变量全都保存在他们自己所定义的命名空间里面,且命名空间分别交nodeA nodeB nodeC,即使各自的命名空间里面有相同的变量,也不会收到任何影响
命名空间的定义
1、正常的命名空间定义
命名空间里面可以定义变量,函数,类型等
namespace Yuan
{
int rand = 0;
void func()
{
printf("func()\n");
}
struct TreeNode
{
struct TreeNode* left;
struct TreeNode* right;
int val;
};
}
int main()
{
printf("%p\n", rand);
printf("%p\n", Yuan::rand);
Yuan::func();
struct Yuan::TreeNode node;
return 0;
}
2、命名空间可以嵌套
命名空间可以嵌套,就想循环语句一样
namespace sql
{
int a = 0;
namespace Yuan
{
int rand = 0;
void func()
{
printf("func()\n");
}
struct TreeNode
{
struct TreeNode* left;
struct TreeNode* right;
int val;
};
}
}
int main()
{
printf("%p\n", rand);
printf("%p\n", sql::Yuan::rand);
sql::Yuan::func();
struct sql::Yuan::TreeNode node;
return 0;
}
3、同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中
这一点怎么来理解呢,比如说在一个项目中我们添加了Stack.h(栈),Stack.cpp(栈),Queue.h(队列),Queue.cpp(队列),里面有很多变量,类型,函数等,现在要把它们放到一个命名空间里面,我们会担心发生命名冲突的问题,怎么处理呢?
这四个文件中分别定义一个 每次为 Yuan 的命名空间,并把文件中所有东西放到命名空间中,这四个命名空间名字相同,但不在一个文件中,编译器会自动把它们合并到一个命名空间中,不用担心命名冲突的问题
namespace Yuan
{
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top; // 栈顶的位置
int capacity; // 容量
}ST;
}
#include "Stack.h"//栈
#include "Queue.h"//队列
int main()
{
Yuan::ST st;
Yuan::StackInit(&st);
Yuan::Queue q;
Yuan::QueueInit(&q);
}
命名空间的使用
1、加命名空间名称及作用域限定符
int main()
{
printf("%d\n", Yuan::a);
return 0;
}
前面已经介绍了通过域作用符来进行命名空间的使用,这也是最基本最简单的一种命名空间使用方式,可这个方式如果在命名空间使用比较多的时候就显得有些繁琐了,可能这里没有加域作用符,那里又没有加,导致一堆bug,所以就有了以下三种方式
2、使用using将命名空间中某个成员引入
using Yuan::b;
int main()
{
printf("%d\n", Yuan::a);
printf("%d\n", b);
return 0;
}
3、使用using namespace 命名空间名称 引入
using namespce N;
int main()
{
printf("%d\n", Yuan::a);
printf("%d\n", b);
Add(10, 20);
return 0;
}
4、使用using namespace std C++标准库命名空间 引入
前面两种相对就比较保守,这个就是很大胆的直接展开C++全部标准库,虽然是很大程度上避免了一定的冲突,但是这样全部展开,就可能存在冲突的风险,所以直接全部展开不推荐使用,如果要用应该是这样写
#include<iostream>
//using namespace std
int main()
{
std::cout << "hello world" << std::endl;
return 0;
}