命名空间

一个中大型软件往往由多名程序员共同开发,会使用大量的变量和函数,不可避免地会出现变量或函数的命名冲突,当所有人的代码都测试通过,没有问题时,但将它们结合到一起就有可能会出现命名冲突
namespace关键字用来定义一个命名空间,语法格式为:

namespace 命名空间名{}

命名空间又称为名字空间,是表达多个变量和多个函数组合成一个组的方法,可以包含变量、函数、类、typedef、#define等,最后由花括号{}包围
主要为了解决名字冲突(用户自定义的类型名,函数名,变量名等)的问题

域解析操作符

::是域解析操作符,在c中用来指明要使用的命名空间
c
标准库中的函数或者对象都是在命名空间std中定义的,所以使用标准函数库中的函数或对象都要使用std来限定
当然也可以引入命名空间(using namespace std),在之后语句省略std,不用的话如果定义同名变量则该命名空间的变量会被局部变量覆盖掉

c++输入输出示例程序:输入十个整数,对其进行冒泡排序后输出

#include<iostream>
using namespace std;
int main()
{
const int n=10;
int num[n],i,j,tmp;
for(i=0;i<n;i++)
{
    cin>>num[i];
}
for(i=1;i<n;i++)
{
    for(j=0;j<n-i;j++)
    {
        if(num[j]>num[j+1])
        {
            tmp=num[j];
            num[j]=num[j+1];
            num[j+1]=tmp;
        }
    }
}
for(i=0;i<n;i++)cout<<num[i]<<" ";
} 

命名空间与作用域

站在编译和链接的角度,代码中出现的变量名、函数名、类名等都是一种符号(Symbol)。有的符号可以指代一个内存位置,例如变量名、函数名;有的符号仅仅是一个新的名称,如typedef定义的类型别名。一个命名空间是一个作用域,在不同名字空间中命名相同的符号代表不同的实体,使用变量、函数时要指明它们所在的命名空间,基本格式为:

命名空间名::变量名,命名空间名::函数名,等等

除了在使用时加上域解析操作符声明命名空间,还可以采用using关键字提前声明变量,例:

using std::cout;
int main()
{
    int a;
    std::cin>>a;
    cout<<a<<std::endl;
}

using声明以后的程序中如果出现了未指明命名空间的符号,就使用声明的命名空间中的成员
如果需要使用其他命名空间的符号,仍然可以使用作用域符来指定
using声明不仅可以针对命名空间中的一个成员,也可以用于声明整个命名空间,如:using namespace std;
此时可以直接使用该命名空间中的成员

命名空间可以在两个地方被定义:在全局范围层次或者是在另一个命名空间中被定义(形成一个嵌套名字空间),例:

namespace xu{namespace shao {namespace qian{int a;}}}
int main()
{
    xu::shao::qian::a=10;
}

不能在函数和类的内部定义命名空间

实际上,每个程序中存在一个全局命名空间,是隐式声明的
可以用作用域操作符引用全局命名空间的成员,由于全局命名空间是隐含的,所以没有名字,如:

::membername;

注意:虽然可以用作用域符标识命名空间,但命名空间与作用域是完全不同的概念,不能搞混

  • 实际上命名空间更类似于一个将不同变量、函数、宏、类等等程序要素名字的声明和定义打包起来的一个逻辑空间,这一点很像头文件的作用,区别在于:
    • 头文件是物理上的概念,是一个实际的文件,而命名空间通常是在头文件中定义的一个逻辑单元,相当于对其中一部分内容进行标识
    • c++支持在多个头文件中定义同名命名空间,但其中成员不能重复(编译器自动将其合为一个)
  • 另一方面,命名空间的定义形式与类很像,其使用方式也与类相似,区别在于:
    • 首先命名空间的粒度更大,其中可以包含类的定义甚至还能嵌套定义,并且可以重复定义以向其中追加内容,而类定义完毕之后就是完全封闭的
    • 类通常是一种实在事物的抽象,其中定义的成员代表了这类事物的性质,是oop的基本建模工具,反观命名空间更像是一组相关事物或工具的集合,逻辑上没有类那么紧密
  • 至于作用域则是一个编译层面上的更抽象的概念,不是程序实体,它描述的是一个名字在程序中的使用范围

标准命名空间(std)

c是在c的基础上开发的,早期的c还不完善,不支持命名空间,并且没有自己的编译器
是通过将c代码翻译成c代码,再通过c编译器完成编译,这个时期的c仍然在使用c的库,如:stdio.h、stdlib.h、string.h等头文件依然有效
此外c也开发了一些新的库增加了自己的头文件,如:iostream.h,fstream.h,complex.h等等
后来c
引入了命名空间的概念,计划重新编写库,将类、函数、宏等都统一纳入一个命名空间
这个命名空间的名字就是std。std是standard的缩写,意思是标准命名空间
为了使用老式c的程序可以继续使用原来的库,新开发的程序可以使用新版的c库,c保留原来的库和头文件可以继续使用,然后把原来的库复制一份,并在此基础上稍加修改,把新增类、函数、宏等纳入命名空间std下,成为了新版**c标准库**,因此这样共存在了两份功能相似的库
为避免头文件重名,新版c++库对头文件的命名做了调整,去掉了后缀.h,如:iostream.h变成iostream,fstream.h变成fstream

  • 对于原来C语言的头文件,也采用同样的方法,但在每个名字前还要添加一个c,如:stdio.h变成cstdio,stdlib.h变成cstdlib(当然继续写.h的也可以,但是标准不推荐)
  • 对于不带.h的头文件,所有的符号都位于命名空间std中,使用时需要声明命名空间std
  • 对于带.h的头文件,没有使用任何命名空间,所有符号都位于全局作用域,这是c++标准所规定的

虽然c几乎完全兼容c,c的头文件在c中依然被支持,但c新增的库更加强大和灵活,一般尽量使用c新增的头文件
很多时候将std直接声明在所有函数外部,这样虽然使用方便,但在中大型项目开发中是不被推荐的,这样做增加了命名冲突的风险,因此一般是在函数内部声明std