c++的命名空间是为了解决重名的问题的,试想这样一个场景,我写了一个函数叫做copy()用来处理一些 can 数据的拷贝,但是在工程的其他位置有一个和我名字一模一样的函数是用来处理其他数据的拷贝,这个时候两个函数名就冲突了,当然不止是函数,有些时候我们定义的一些变量名,类名啥的都可能会存在重复的情况。面对这样的问题的时候,我们可以应用命名空间
来解决这些问题,我们通常把可能重复的一些东西写在自定义的命名空间中,命名空间会为里面的这些函数,类,变量附加一些上下文信息从而取分,就像是一个文件夹下不能
存在两个相同名字的文件,那我们可以建立一个文件夹然后在这个文件夹下放入我们的文件,
命名空间就相当于这个新建的文件夹。
定义命名空间(namespace)
使用 namespace 关键字可以定义一个名字空间,例如:
#include<iostream>
using namespace std;
namespace jiuyue
{
int a = 8; //定义变量
class demo //定义类
{
private:
int b;
public:
demo(){b=0;};
~demo(){};
int A(int x){return x;}
};
int B() //定义函数
{
return 10;
}
namespace qiyue //定义名字空间
{
int c;
}
}
int main(int argc, char const *argv[])
{
jiuyue::a = 10; //使用 jiuyue 里面的变量
cout<<jiuyue::B()<<endl; //使用 jiuyue 里面的函数
jiuyue::demo s;
cout<<s.A(9)<<endl; //使用 jiuyue 里面的类
jiuyue::qiyue::c = 2; //使用 jiuyue 里面的名字空间
return 0;
}
我们定义一个名字空间为 jiuyue,我们可以在里面去定义和初始化变量,类和函数,也可以定 义名字空间,在使用的时候我们使用作用域符号::去索引我们名字空间中的这些内容就可以使用 了。
引用命名空间(using)
using 关键字可以对命名空间进行引用,比如有一个主文件夹,也就是我们的主进程空间,里面有一个子文件夹 叫 sdt,也就是我们的 std 名字空间,std 这个子文件夹中有一个 cin 文件,一般我们是没法直接在主文件夹是没法 cin 的,而要进入 std 子文件夹中去使用它,也就是我们在使用 cin 的时候需要 std::cin 去索引它,而 using 的作用就是可以将这些子文件中的东西拷贝到主文件中,也就是可以不使用作用域符::去索引就可以使用名字空间中的东西。
#include<iostream>
int main(int argc, char const *argv[])
{
// cout<<"hello word"<<endl; //错误
std::cout<<"hello word"<<std::endl; //正确
return 0;
}
如下图,如果直接访问 std 名字空间中的 cout 和 endl 是会报错的,因为没法找到它,所以要 使用作用域符::去用名字空间索引它才能使用,但是可以通过 using 将这个名字空间的对象拷贝到当前作用域上,那么在使用时就不需要使用名字空间去索引了,如下。
#include<iostream>
using std::cout;
using std::endl;
int main(int argc, char const *argv[])
{
cout<<"hello word"<<endl; //不会报错
return 0;
}
我们经常看到类型下面这样的用法:
using namespace std;
这样的写法确实就是将整个名字空间的内容全部引入到当前作用域,这样做在初学 c++的时候不会有什么问题,可以方便我们直接使用 std 名字空间里面的东西,但是在工作工程中尽量不
要这样使用,将整个名字空间的内容引入无疑会大大加大名字的重复性,反而就让我们的名字空间失去了意义.
匿名名字空间
匿名名字空间提供了类似在全局函数前加 static 修饰带来的限制作用域的功能。比如,在两个源文件中定义了相同的全局变量(或函数),就会发生重定义的错误。如果将它们声明为全局静态变量(函数)就可以避免重定义错误。在 C++中,除了可以使用 static 关键字避免全局变量(函数)的重定义错误,还可以通过匿名名字空间的方式实现。例如
//1.cpp
#include<iostream>
int dev = 10;
extern void fun();
int main(int argc, char const *argv[])
{
std::cout << dev <<std::endl;
//void fun();
fun();
return 0;
}
//2.cpp
#include<iostream>
namespace
{
int dev = 0;
};
void fun(){std::cout<<dev<<std::endl;}
未命名的名字空间中定义的变量(或函数)只在包含该名字空间的文件中可见,但其中的变量的生存期却从程序开始到程序结束。如果有多个文件包含未命名的名字空间,这些名字空间是不相关的,即使这些名字空间中定义了同名的变量(函数),这些标识符也代表不同的对象。也就是说我在头文件中定义了一个匿名空间,那么包含这个匿名空间的文件将都会获得这个匿名空间里的对象,但是都是独立的静态对象。
注意事项
1.
一个名字空间可以在多个头文件或源文件中实现,成为分段定义。如果想在当前文件访问定义在另一个文件中的同名名字空间内的成员变量,需要在当前文件的名字空间内部进行申明。如标准 C++
库中的所有组件都是在一个被称为
std
的名字空间中声明和定义的。这些组件当然分散在不同的头文件和源文件中。
2.
名字空间内部可以定义类型、函数、变量等内容,但名字空间不能定义在类和函数的内部。
3.
在一个名字空间中可以自由地访问另一个名字空间的内容,因为名字空间并没有保护级别的限制。
4.
不能在名字空间的定义中声明另一个嵌套的子命名空间,只能在命名空间中定义子命名空间。也就是你可以在名字空间中定义一个名字空间,但是不能声明一个名字空间
5.
名字空间的成员,可以在命名空间的内部定义,也可以在名字空间的外部定义,但是要在名字空间进行声明。
6.
名字空间在进行分段定义时,不能定义同名的变量,否则连接出现重定义错误。因为名字空间不同于类,具有外部连接的特性。由于外部连接特性,请不要将名字空间定义在头文件,因为当被不同的源文件包含时,会出现重定义的错误。
7.
为了避免命名空间的名字与其他的命名空间同名,可以用较长的标识符作为命名空间的名字。但是书写较长的命名空间名时,有些冗余,因此,我们可以在特定的上下文环境中给命名空间起一个相对简单的别名。 例如
namespace MyNewlyCreatedSpace{
void show(){
std::cout<<"a function within a namespace"<<std::endl;
}
}
int main(int argc,char* argv[])
{
namespace sp=MyNewlyCreatedSpace;
sp::show();
}