目录
命名空间的由来
C语言中存在着某些问题:全局变量可能会面临命名冲突的问题
#include <stdio.h>
int rand = 10;
int main()
{
printf("%d", rand);
return 0;
}
以上代码是正常运行的(打印10)
但如果再包一个头文件<stdlib.h>
就会报错(包头文件的原理是将头文件里面的东西直接拷贝过来)
因为在<stdlib.h>中,存在着一个与rand同名的变量,名字之间产生了冲突
这些问题有可能会出现在:
程序员写的变量名和库里的变量名冲突
在公司里面,两个程序员都写了某一个重名的变量
C语言未能解决该问题(只能相互改名)
而c++解决这个问题的方式就是添加命名空间(不同的域里面可以定义同名的变量)
命名空间
概念
命名空间(Namespace)是 C++ 中用来避免命名冲突的一种机制;
通过命名空间,你可以创建一个独立的作用域,在命名空间中可以定义变量、函数、类等(因为这三个东西命名方面都可能会冲突)
这些定义在命名空间内部的标识符在命名空间外部是不可见的,从而避免了与其他部分代码的命名冲突。
简要说明,即:
全局域:野生的菜地
局部域:自家的菜地
命名空间:张大爷的菜地
如果没有张大爷的允许,就不允许去张大爷家的菜地里摘菜(访问)
目前我们学习的域:全局域,局部域←影响生命周期和访问,命名空间域←只会影响访问
语法
namespace ns1 (命名空间的名字)
{
int rand = 10;(变量)
struct Node(结构体)
{
char* name;
int age;
};
int Add(int a, int b) (函数)
{
return a + b;
}
}
为什么可以避免命名冲突?
表层角度:
将两个同名的变量分别用两个不同的命名空间圈起来(此时的x还是全局变量,只不过是放到了命名空间域里面,做了名字的隔离)
所以,命名空间解决了名字隔离、命名冲突的问题
底层角度:
编译器使用一个变量时,会进行搜索,
编译器的搜索原则:
未指定域:
- 首先搜索局部域,
- 再搜索全局域,找完了还是没找到的话就会报错
指定了域:
- 如果指定了域,就直接到指定的域里搜索指定变量
所以,命名空间解决了变量的搜索问题
问:如果两个命名空间冲突了呢?
答:命名空间之间可以同名
当两个命名空间重名时,两个就会合并在一起
所以,如果两个重名的命名空间里面写了同名的变量,会因为合并到了同一个命名空间而产生重定义的报错
使用方法
命名空间的用法一:指定命名空间
域作用限定符( ::)
用法:
namespace ns1 (命名空间的名字)
{
int rand = 10;(变量)
struct Node(结构体)
{
char* name;
int age;
};
int Add(int a, int b) (函数)
{
return a + b;
}
}
///以上内容均相同
int main()
{
printf("%d", ns1::rand);(打印10)
ns1::Add(1 , 2);
struct ns1::Node x;(要注意ns1:: 不是放在开头的)
return 0;
}
(如果说我们想要去访问全局的变量而不是局部的变量,可以通过域作用限定符来指定)
int rand = 10;
int main()
{
int rand = 3;printf("%d", ::rand);
//↑↑↑
//打印10
return 0;
}
命名空间的用法二:全展开命名空间
语法:
namespace ns1 (命名空间的名字)
{
int rand = 10;(变量)
struct Node(结构体)
{
char* name;
int age;
};
int Add(int a, int b) (函数)
{
return a + b;
}
}
///以上部分均相同
using namespace ns1;(以下的内容不用再指定命名空间)
int main()
{
printf("%d", rand);(打印10)
Add(1 , 2);
struct Node x;
return 0;
}
张大爷把自家的菜地公开了,所有人都可以去张大爷家摘菜了
(展开相当于让命名空间内的东西变成全局的)
原理:放开了访问权限
与头文件的展开不同
头文件展开:将头文件直接拷贝到当前文件下
命名空间展开:将访问权限公开
使用场景:平时自己写练习代码,为了方便可以全展开
优势:大量访问时比较方便
劣势:全展开相当于直接变成全局的,会产生命名冲突的问题,慎用!
命名空间的用法三:部分展开命名空间
语法:
namespace ns1 (命名空间的名字)
{
int rand = 10;(变量)
struct Node(结构体)
{
char* name;
int age;
};
int Add(int a, int b) (函数)
{
return a + b;
}
}
///以上部分均相同
using ns1::rand;(以下的内容只有rand不用指定命名空间)
int main()
{
printf("%d", rand);(打印10)
ns1::Add(1 , 2);
struct ns1::Node x;
return 0;
}
使用场景:
如果某个头文件的函数或者变量等需要反复使用
但是每次使用都指定命名空间会很麻烦
又不敢去展开全部
就可以把常用的展开
命名空间的用法四:命名空间里面嵌套命名空间
语法:
namespace ns1 (命名空间的名字)
{
int rand = 10;(变量)
namespace ns2
{
int rand = 10000;(变量)
}
}
///以上部分均相同
int main()
{
printf("%d", ns1::rand);(打印10)
printf("%d", ns1::ns2::rand);(打印10000)
return 0;
}
使用场景:若公司项目要求将某些函数以及部分内容包含在同一个命名空间中,就可以在命名空间里面再套一个命名空间(怎么嵌套就怎么访问)
使用建议
命名空间要么不展开,要么只展开部分常用的
实际的应用场景
在c++中,实际的应用 场景:
在正常的项目中,可能会写出很多的.cpp文件(有可能是单独自己的,也有可能包了其他人的)
例如说写了一个链表和一个队列
----------------deque.h---------------
struct Node
{
int val;
struct Node* next;
struct Node* prev;
};
struct deque
{
struct Node* head;
int length;
};
void Init(struct deque* head)
{};
void Push(int val, struct deque* head)
{};
-------------deque.h-------------------
↑↓如果将这两个头文件同时包在一起,由于他们的函数名Init和Push重名了,以及他们的Node结构体也重名了,结果就会报错
-------------Queue.h-------------------
struct Node
{
int val;
struct Node* next;
};
struct Queue
{
struct Node* head;
struct Node* tail;
int size;
};
void Init(struct Queue* pq)
{};
void Push(struct Queue* pq, int x)
{};
-------------Queue.h-------------------
解决方法:
将两个结构体用不同的命名空间来包含起来
----------------deque.h---------------
struct Node
{
int val;
struct Node* next;
struct Node* prev;
};
struct deque
{
struct Node* head;
int length;
};
void Init(struct deque* head)
{};
void Push(int val, struct deque* head)
{};
-------------deque.h-------------------
要使用上面的结构或者函数,只需要加上预作用限定符指定访问即可