命名空间
c++中存在着大量的头文件,每个头文件中大量的函数或变量有自己的定义域,为了防止命名冲突引入了命名空间概念。这与java有很大不同,在java中很少出现冲突,java中很多方法和变量定义在类的内部,使用时直接使用类调用,如果还冲突则可以使用包名来限定。
在命名空间出现之前,整个C++库都是定义在全局命名空间中的(这当然也是唯一的命名空间)。引入命名空间后,C++库就被定义到自己的名称空间中了,称之为std。这样就减少了名称冲突的可能性。
声明命名空间
namespace 关键字使得我们可以通过创建作用范围来对全局命名空间进行分隔。本质上来讲,一个命名空间就定义了一个范围。定义命名空间的基本形式如下:
namespace 名称{//声明}
在命名空间中定义的任何东西都局限于该命名空间内。
namespace CounterNameSpace
{
int upperbound;
int lowerbound;
class counter
{
int count;
public:
counter(int n)
{
if ( n < upperbound)
{
count = n;
}
else
{
count = upperbound;
}
}
void reset( int n )
{
if ( n <= upperbound )
{
count = n;
}
}
int run()
{
if ( count > lowerbound )
{
return count--;
}
else
{
return lowerbound;
}
}
};
}
既然命名空间定义了一个范围,那么我们在命名空间之外就需要使用范围解析运算符来引用命名空间中的对象。例如,在命名空间CounterNameSpace定义的范围之外给upperbound赋值为10,就必须这样写:
CounterNameSpace::upperbound = 10;
或者在CounterNameSpace定义的范围之外想要声明一个counter类的对象就必须这样写:
CounterNameSpace::counter obj;
一般来讲,在命名空间之外想要访问命名空间内部的成员需要在成员前面加上命名空间和范围解析运算符。
相同的空间名称是可以被多次声明的,这种声明向相互补充的。这就使得命名空间可以被分割到几个文件中甚至是同一个文件的不同地方中。
namespace NS
{
int i;
}
//...
namespace NS
{
int j;
}
using
如果在程序中需要多次引用某个命名空间的成员,那么按照之前的说法,我们每次都要使用范围解析符来指定该命名空间,这是一件很麻烦的事情。为了解决这个问题,人们引入了using关键字。using语句通常有两种使用方式:
using namespace 命名空间名称;
using 命名空间名称::成员;
第一种形式中的命名空间名称就是我们要访问的命名空间。该命名空间中的所有成员都会被引入到当前范围中。也就是说,他们都变成当前命名空间的一部分了,使用的时候不再需要使用范围限定符了。第二种形式只是让指定的命名空间中的指定成员在当前范围中变为可见。我们用前面的CounterNameSpace来举例,下面的using语句和赋值语句都是有效的:
using CounterNameSpace::lowerbound; //只有lowerbound当前是可见的
lowerbound = 10; //这样写是合法的,因为lowerbound成员当前是可见的
using CounterNameSpace; //所有CounterNameSpace空间的成员当前都是可见的
upperbound = 100; //这样写是合法的,因为所有的CounterNameSpace成员目前都是可见的
命名空间的作用域仍在单个文件中
///使用using
#include <iostream>
using namespace std;
namespace CounterNameSpace
{
int upperbound;
int lowerbound;
class counter
{
int count;
public:
counter(int n)
{
if ( n < upperbound)
{
count = n;
}
else
{
count = upperbound;
}
}
void reset( int n )
{
if ( n <= upperbound )
{
count = n;
}
}
int run()
{
if ( count > lowerbound )
{
return count--;
}
else
{
return lowerbound;
}
}
};
}
int main()
{
//这里只是用CounterNameSpace中的upperbound
using CounterNameSpace::upperbound;
//此时对upperbound的访问就不需要使用范围限定符了
upperbound = 100;
//但是使用lowerbound的时候,还是需要使用范围限定符的
CounterNameSpace::lowerbound = 0;
CounterNameSpace::counter ob1(10);
int i;
do
{
i = ob1.run();
cout << i << " ";
}while( i > CounterNameSpace::lowerbound);
cout << endl;
//下面我们将使用整个CounterNameSpace的命名空间
using namespace CounterNameSpace;
counter ob2(20);
do
{
i = ob2.run();
cout << i << " ";
}while( i > CounterNameSpace::lowerbound);
cout << endl;
ob2.reset(100);
lowerbound = 90;
do
{
i = ob2.run();
cout << i << " ";
}while( i > lowerbound);
return 0;
}
上面的程序还为我们演示了重要的一点:当我们用using引入一个命名空间的时候,如果之前有引用过别的命名空间(或者同一个命名空间),则不会覆盖掉对之前的引入,而是对之前引入内容的补充。也就是说,到最后,上述程序中的std和CounterNameSpace这两个命名空间都变成全局空间了。
没有名称的命名空间
有一种特殊的命名空间,叫做未命名的命名空间。这种没有名称的命名空间使得我们可以创建在一个文件范围里可用的命名空间。其一般形式如下:
namespace
{
//声明
}
std命名空间
标准C++把自己的整个库定义在std命名空间中:
using namespace std;
这样写是为了把std命名空间的成员都引入到当前的命名空间中,以便我们可以直接使用其中的函数和类,而不用每次都写上std::。
命名空间冲突
不要盲目的在全局展开std,可能会导致全局的命名变量冲突,如std命名空间的bind和sys/socket.h中bind发生冲突,与java使用包名具体的某个类相似,这里使用::这种形式限定到底使用哪个命名空间/全局空间
如果想用std的bind,就在bind前加std::,例如:int result = std::bind(listen_fd, (struct sockaddr *)&server, sizeof(struct sockaddr));
如果想用sys/socket.h中的bind,就在bind前加::,例如:int result = ::bind(listen_fd, (struct sockaddr *)&server, sizeof(struct sockaddr));