什么是命名空间
命名空间是一种用来避免命名冲突的机制,它可以将一段代码的名称隔离开,使其与其他代码的名称不冲突;
命名空间的原理是将一个全局的作用域分成一个个命名空间,每个命名空间是个单独的作用域,同时若是在同一个作用域内可能出现的命名冲突也不会造成影响,有效避免了命名空间的污染;
命名空间的作用
在学习C语言的过程中我们知道有一个函数叫做rand函数,该函数用于生成伪随机整数;
#include<stdio.h> int rand = 10; int main() { printf("%d\n",rand); return 0; }
上面的代码在.c文件下的情况执行会得出什么结果?
代码被运行成功且没有什么错误;
但若是在包含rand函数所在的头文件为前提的结果是什么?
当包含了rand函数所在的头文件即报错;
原因是,在程序运行时,计算机先会在局部寻找变量,当局部内未找到时则回去全局找;
在全局找时找到了两个 rand (一个为头文件内的rand函数,一个为所创建的变量rand);
此时也不知该打印哪一个rand,这就叫命名冲突;
但在C++中,出现了一个新的概念叫做命名空间,一般来说,rand这类的函数都存放于C++中标准库类型对象的命名空间std中;
若是在编译器中执行这段代码结果是什么;
#include<iostream>//标准输入输出流头文件 using namespace std;//展开标准库命名空间std namespace Sweet{//设置命名空间且名为Sweet int rand = 10; } int main() { cout << rand << endl;//打印rand cout << Sweet::rand << endl;//打印Sweet中的rand(::符号为作用域限定符) return 0; }
打印出来的结果没有报错,原因是在这里并没有发生命名冲突,在这段代码中使用了
namespace Sweet{ int rand = 10; }
设置了一个名为Sweet的命名空间,且在命名空间内声明变量rand,在打印过程中就相当于虽然重名但是所在班级不同的学生;
但此时的rand仍然处于全局空间中,只是隐藏在名为Sweet的命名空间内;
若是运行下面的代码,则结果是什么?
#include<iostream> using namespace std; namespace Sweet{ int rand = 10; } using namespace Sweet; int main() { cout << rand << endl; cout << Sweet::rand<<endl; return 0; }
如何定义命名空间
从上面可知命名空间的作用,既然命名空间能够有效防止命名冲突,那该如何定义命名空间?
在定义命名空间时,应该使用 namespace 修饰且在该修饰词后定义名字
并在其后跟**{ }**
在{ }内的元素即为该命名空间的成员;
#include<iostream> using namespace std;//展开std命名空间 namespace A1 {//定义一个名为A1的命名空间 int a = 0; int b = 20;//变量 void Add()//函数 { cout << "IsAddFunc1" << endl; } struct TestNode//结构体 { int test1; double test2; }; } namespace A2 {//定义一个名为A2的命名空间 int a = 10; int b = 21; void Add() { cout << "IsAddFunc2" << endl; } struct TestNode { int test3; double test4; }; } int main() { cout << "A1::b = " << A1::b << endl;//打印A1中的b cout << "A2::b = " << A2::b << endl;//打印A2中的b A1::Add();//调用A1中的Add函数 A2::Add();//调用A2中的Add函数 struct A1::TestNode AT1;//声明结构体变量 struct A2::TestNode AT2; cout << &AT1 << endl;//打印结构体变量地址 cout << &AT2 << endl; return 0; }
从该段代码可知命名空间内不仅可以声明(定义)变量,还可以包含函数、类、typedef以及#define宏等等;
命名空间的种类
在一般情况下,命名空间还可以进行嵌套,即可以在一个命名空间内嵌再嵌套命名空间,以此类推;
#include<iostream> using namespace std; namespace A1{ int mi = 0; int qi = 5; namespace A2 { int qi = 10; namespace A3 { int mi = 25; } } } int main() { cout << "A1::mi = " << A1::mi << endl; cout << "A1::qi = " << A1::qi << endl; cout << "A1::A2::qi = " << A1::A2::qi << endl; cout << "A1::A2::A3::mi = " << A1::A2::A3::mi << endl; return 0; }
该处A1命名空间内嵌套着A2,A2嵌套A3;
注意: 命名空间只能在全局范围定义!!
故上图的使用方法为错误方法
如何使用命名空间内的成员
既然命名空间可以防止发生命名冲突,那放在命名空间的成员应该如何使用?
作用域限定符
在一般情况下,若是想使用命名空间内的成员,可以使用作用域限定符 ::(域解析操作符)
#include<iostream> using namespace std; namespace A1{ int mi = 0; int qi = 5; namespace A2 { int qi = 10; namespace A3 { int mi = 25; } } } int main() { cout << "A1::mi = " << A1::mi << endl; cout << "A1::qi = " << A1::qi << endl; cout << "A1::A2::qi = " << A1::A2::qi << endl; cout << "A1::A2::A3::mi = " << A1::A2::A3::mi << endl; return 0; }
该段代码即使用了作用域限定符;
该符号的作用是通知编译器应从作用域限定符左侧的名字所示的作用域中寻找右侧那个名字,即指定访问哪个名字空间的哪个成员。
命名空间展开
除了可以使用作用域限定符进行操作以外还可以将命名空间展开,同时命名空间展开有两种
①命名空间全部展开
②命名空间部分展开
命名空间全部展开
命名空间全部展开需要使用
cpp using namespace xxx;
进行修饰;
#include<iostream> using namespace std; int main() { cout<<"using namespace std"<<endl; return 0; }
如上代码将标准库中的std命名空间展开,即可以在不受作作用域限定符的限制使用std命名空间内的cout与endl;
但若是将命名空间全部展开则也有一些麻烦;
例如:
该处将两个命名空间都进行了展开,导致程序在运行中从全局范围中找到了两个rand从而导致的“悲剧”;
故一般情况下,在项目中不支持以命名空间全部展开的方式使用命名空间内的成员;
命名空间部分展开
除了命名空间全部展开以外还有一种方式即相对的进行命名空间部分展开;
using XXX :: xxx;
以该方式将命名空间中的部分分支进行展开,即可以在不全部展开以及不使用作用域限定符的情况下使用命名空间内的成员;
#include<iostream> using std::cout;//进行命名空间部分展开将std命名空间内的cout以及endl进行部分展开 using std::endl;//命名展开后可以在不影响该命名空间以内其他成员的情况下使用cout与endl int main() { cout << "Hello World" << endl;//打印"Hello World" return 0; }
总结
①命名空间可以避免命名冲突问题;
②命名空间可以进行嵌套;
③命名空间必须在全局范围内定义,不可再局部范围内定义;
④在使用命名空间内的成员时共有三种方法(作用域限定符、命名空间全部展开、命名空间部分展开),在一般练习中尽量不要进行全部展开;