为什么需要命名空间
命名空间是ANSI C++引入的可以由用户命名的作用域,用来处理程序中常见的同名冲突。
C语言中定义了3个层次的作用域,即文件(编译单元) 、函数和复合语句。C++又引入了类作用域,类是出现在文件内的。在不同的作用域中可以定义相同名字的变量,互不干扰,系统能够区别它们。
下面先简单分析一下作用域的作用,然后讨论命名空间的作用。
如果在文件中定义了两个类,在这两个类中可以有同名的函数。在引用时,为了区别,应该加上类名作为限定,如
class A//声明A
 {public:
void fun1( );         //声明A类中的fun1函数
  private:
int i;
 };
void A::fun1( )             //定义A类中的fun1函数
 {
//
 }
class B                   //声明B
 {public:
void fun1( );          //B类中也有fun1函数
void fun2( );
 };
void B::fun1( )             //定义B类中的fun1函数
 {
   //
 }
这样不会发生混淆。
在文件中可以定义全局变量(global variable),它的作用域是整个程序。如果在文件A中定义了一个变量a
int a=3;
在文件B中可以再定义一个变量a
int a=5;
在分别对文件A和文件B进行编译时不会有问题。但是,如果一个程序包括文件A和文件B,那么在进行连接时,会报告出错,因为在同一个程序中有两个同名的变量,认为是对变量的重复定义。问题在于全局变量的作用域是整个程序,在同一作用域中不应有两个或多个同名的实体(entity),包括变量、函数和类等。 
可以通过extern声明同一程序中的两个文件中的同名变量是同一个变量。如果在文件B中有以下声明:
extern int a;
表示文件B中的变量a是在其他文件中已定义的变量。由于有此声明,在程序编译和连接后,文件A的变量a的作用域扩展到了文件B。如果在文件B中不再对a赋值,则在文件B中用以下语句输出的是文件A中变量 a的值:
cout<<a;//得到a的值为3
在简单的程序设计中,只要人们小心注意,可以争取不发生错误。但是,一个大型的应用软件,往往不是由一个人独立完成的,假如不同的人分别定义了类,放在不同的头文件中,在主文件(包含主函数的文件)需要用这些类时,就用#include命令行将这些头文件包含进来。由于各头文件是由不同的人设计的,有可能在不同的头文件中用了相同的名字来命名所定义的类或函数。这样在程序中就会出现名字冲突。
 
1.名字冲突。
程序员甲在头文件header1.h中定义了类Student和函数fun
//header1.h (头文件1,设其文件名为cc14-4-h1.h)
#include <string>
#include <cmath>
using namespace std;
class Student//声明Student
{public:
   Student(int n,string nam,char s)
{num=n;name=nam;sex=s;}
   void get_data( );
private:
int num;
   string name;
   char sex;
 };
void Student::get_data( )               //成员函数定义
{cout<<num<< <<name<< <<sex<<endl;
}
double fun(double a,double b)          //定义全局函数(即外部函数)
{return sqrt(a+b);}
main函数所在的文件中包含头文件header1.h:
#include <iostream>
#include cc14-4-h1.h    //注意要用双引号,因为文件一般是放在用户目录中的
using namespace std;
int main( )
{Student stud1(101,Wang,18);  //定义类对象stud1
   stud1.get_data( );
   cout<<fun(5,3)<<endl;
   return 0;
   }
程序能正常运行,输出为
101 Wang 18
2.82843
如果程序员乙写了头文件header2.h,在其中除了定义其他类以外,还定义了类Student和函数fun,但其内容与头文件header1.h中的Student和函数fun有所不同。
//header2.h (头文件2,设其文件名为cc14-4-h2.h)
#include <string>
#include <cmath>
using namespace std;
class Student//声明Student
{public:
   Student(int n,string nam,char s)      //参数与header1中的student不同
{num=n;name=nam;sex=s;}
   void get_data( );
private:
   int num;
   string name;
   char sex;                          //此项与header1不同
};
void Student::get_data( )               //成员函数定义
{cout<<num<< <<name<< <<sex<<endl;
}
double fun(double a,double b)        //定义全局函数
 {return sqrt(a-b);}                 //返回值与header1中的fun函数不同
//头文件2中可能还有其他内容
假如主程序员在其程序中要用到header1.h中的Student和函数fun,因而在程序中包含了头文件header1.h,同时要用到头文件header2.h中的一些内容,因而在程序中又包含了头文件header2.h。如果主文件(包含主函数的文件)如下:
//main file
#include <iostream>
#include cc14-4-h1.h//包含头文件1
#include cc14-4-h2.h        //包含头文件2
using namespace std;
int main( )
{Student stud1(101,Wang,18);
stud1.get_data();
cout<<fun(5,3)<<endl;
return 0;
 }
这时程序编译就会出错。 因为在预编译后,头文件中的内容取代了对应的#include命令行,这样就在同一个程序文件中出现了两个Student类和两个fun函数,显然是重复定义,这就是名字冲突,即在同一个作用域中有两个或多个同名的实体。
不仅如此,在程序中还往往需要引用一些库,为此需要包含有关的头文件。如果在这些库中包含有与程序的全局实体同名的实体,或者不同的库中有相同的实体名,则在编译时就会出现名字冲突。
为了避免这类问题的出现,人们提出了许多方法,例如: 将实体的名字写得长一些;把名字起得特殊一些,包括一些特殊的字符;由编译系统提供的内部全局标识符都用下划线作为前缀,如_complex(),以避免与用户命名的实体同名;由软件开发商提供的实体的名字用特定的字符作为前缀。但是这样的效果并不理想,而且增加了阅读程序的难度,可读性降低了。
C语言和早期的C++语言没有提供有效的机制来解决这个问题,没有使库的提供者能够建立自己的命名空间的工具。人们希望ANSI C++标准能够解决这个问题,提供一种机制、一种工具,使由库的设计者命名的全局标识符能够和程序的全局实体名以及其他库的全局标识符区别开来。
 
 
什么是命名空间
为了解决上面这个问题,ANSI C++增加了命名空间(namespace)。所谓命名空间,实际上就是一个由程序设计者命名的内存区域。程序设计者可以根据需要指定一些有名字的空间域,把一些全局实体分别放在各个命名空间中,从而与其他全局实体分隔开来。如
namespace ns1//指定命名空间ns1
{int a;
double b;
}
现在命名空间成员包括变量ab,注意ab仍然是全局变量,仅仅是把它们隐藏在指定的命名空间中而已。
如果在程序中要使用变量ab,必须加上命名空间名和作用域分辨符“::”,如ns1::ans1::b。这种用法称为命名空间限定(qualified),这些名字(ns1::a)称为被限定名(qualified name)C++中命名空间的作用类似于操作系统中的目录和文件的关系。
命名空间的作用是建立一些互相分隔的作用域,把一些全局实体分隔开来,以免产生名字冲突。
可以根据需要设置许多个命名空间,每个命名空间名代表一个不同的命名空间域,不同的命名空间不能同名。这样,可以把不同的库中的实体放到不同的命名空间中。过去我们用的全局变量可以理解为全局命名空间,独立于所有有名的命名空间之外,它是不需要用namespace声明的,实际上是由系统隐式声明的,存在于每个程序之中。
在声明一个命名空间时,花括号内不仅可以包括变量,而且还可以包括以下类型:
    变量(可以带有初始化)
    常量;
    函数(可以是定义或声明)
    结构体;
    类;
    模板;
    命名空间(在一个命名空间中又定义一个命名空间,即嵌套的命名空间)
例如
namespace ns1
{const int RATE=0.08;//常量
double pay;                         //变量
double tax( )                        //函数
{return a*RATE;}
namespace ns2                       //嵌套的命名空间
    {int age;}
}
如果想输出命名空间ns1中成员的数据,可以采用下面的方法:
cout<<ns1::RATE<<endl;
cout<<ns1::pay<<endl;
cout<<ns1::tax()<<endl;
cout<<ns1::ns2::age<<endl;//需要指定外层的和内层的命名空间名