命名空间是用来组织和重用代码的编译单元。如同名字一样的意思,NameSpace(名字空间),之所以出来这样一个东西,是因为人类可用的单词数太少,并且不同的人写的程序不可能所有的变量都没有重名现象,对于库来说,这个问题尤其严重,如果两个人写的库文件中出现同名的变量或函数(不可避免),使用起来就有问题了,为了解决这个问题,引入了名字空间这个概念,通过使用 namespace xxx;你所使用的库函数或变量就是在该名字空间中定义的,这样一来就不会引起不必要的冲突了。
所谓namespace,是指标识符的各种可见范围。C++标准程序库中的所有标识符都被定义于一个名为std的namespace中。
using关键字
如果在程序中需要多次引用某个命名空间的成员,那么按照之前的说法,我们每次都要使用范围解析符来指定该命名空间,这是一件很麻烦的事情。为了解决这个问题,人们引入了using关键字。using语句通常有两种使用方式:
using namespace 命名空间名称;
using 命名空间名称::成员;
第一种形式中的命名空间名称就是我们要访问的命名空间。该命名空间中的所有成员都会被引入到当前范围中。也就是说,他们都变成当前命名空间的一部分了,使用的时候不再需要使用范围限定符了。第二种形式只是让指定的命名空间中的指定成员在当前范围中变为可见。我们用前面的CounterNameSpace来举例,下面的using语句和赋值语句都是有效的:
using CounterNameSpace::lowerbound; //只有lowerbound当前是可见的
lowerbound = 10; //这样写是合法的,因为lowerbound成员当前是可见的
using CounterNameSpace; //所有CounterNameSpace空间的成员当前都是可见的
upperbound = 100; //这样写是合法的,因为所有的CounterNameSpace成员目前都是可见的
当我们用using引入一个命名空间的时候,如果之前有引用过别的命名空间(或者同一个命名空间),则不会覆盖掉对之前的引入,而是对之前引入内容的补充。也就是说,到最后,上述程序中的std和CounterNameSpace这两个命名空间都变成全局空间了。
std命名空间
标准C++把自己的整个库定义在std命名空间中。这就是本书的大部分程序都有下面代码的原因:
using namespace std;
这样写是为了把std命名空间的成员都引入到当前的命名空间中,以便我们可以直接使用其中的函数和类,而不用每次都写上std::。
当然,我们是可以显示地在每次使用其中成员的时候都指定std::,只要我们喜欢。例如,我们可以显示地采用如下语句指定cout:
std::cout << “显示使用std::来指定cout”;
如果我们的程序中只是少量地使用了std命名空间中的成员,或者是引入std命名空间可能导致命名空间的冲突的话,我们就没有必要使用using namespace std;了。然而,如果在程序中我们要多次使用std命名空间的成员,则采用using namespace std;的方式把std命名空间的成员都引入到当前命名空间中会显得方便很多,而不用每次都单独在使用的时候显示指定
下 面通过例程说明关键字namespace的用法。
#include <conio.h>
#include <iostream.h>
namespace car // 名空间的定义
{
int model;
int length;
int width;
}
namespace plane
{
int model;
namespace size // 名空间的嵌套
{
int length;
int width;
}
}
namespace car // 添加名空间的成员
{
char * name;
}
namespace c=car; // 定义名空间的别名
int Time; // 外部变量属于全局名空间
void main()
{
car::length=3;
// 下面一句错误,故屏蔽掉
// width=2; // 对于非全局变量和当前有效临时变量应该指定名空间
plane::size::length=70;
cout<<"the length of plane is "<<plane::size::length<<"m."<<endl;
cout<<"the length of car is "<<car::length<<"m."<<endl;
// 使用名空间的别名
cout<<"the length of c is "<<c::length<<"m."<<endl;
int Time=1996; // 临时变量,应区别于全局变量
::Time=1997;
cout<<"Temp Time is "<<Time<<endl;
cout<<"Outer Time is "<<::Time<<endl;
// 使用关键字using
using namespace plane;
model=202;
size::length=93;
cout<<model<<endl;
cout<<size::length<<endl;
getch();
}
运行结果:
the length of plane is 70m.
the length of car is 3m.
the length of c is 3m.
Temp Time is 1996
Outer Time is 1997
说明:
1. 从上面可以看出,名空间定义了一组变量和函数,它们具有相同的作用范围。对于不同的
名空间,可以定义相同的变量名或函数名,在使用的时候,只要在变量名或函数名前区分 开不同的名空间就可以了。
2. 名空间可以被嵌套定义,使用时要逐级对成员用名空间限定符:
:来引用。
3. 系统默认有一个全局名空间,它包含了所有的外部变量。这个名空间没有名字,引用这个
名空间里的变量时要使用名空间限定符:
:,前面没有名字。在不使用名空间的情况下,我
们知道,不可以在不同文件中定义相同名字的外部变量,这是因为它们属于同一个全局名 空间,名字不可以重复。
4.
可以给名空间取一个别名。一般别名是一个比较短的名字,来简化编程。
在前面的例程中可以看到,为了使用时的方便,又引入了关键字using。利用using声明可以在引用名空间成员时不必使用名空间限定符::。此外,关键字namespace和using的使用,对函数重载有一定的影响。
下面通过例程进行具体说明。
#include <conio.h>
#include <iostream.h>
namespace car // 名空间的定义
{
void ShowLength(double len) // 参数类型为d o u b l e
{
cout<<"in car namespace: "<<len<<endl;
}
}
namespace plane // 名空间的定义
{
void ShowLength(int len) // 参数类型为i n t
{
cout<<"in plane namespace: "<<len<<endl;
}
}
void main()
{
using namespace car;
ShowLength(3);
ShowLength(3.8);
using namespace plane;
ShowLength(93);
ShowLength(93.75);
getch();
}
运行结果:
<span style="color:#333333;">in car namespace: 3
in car namespace: 3.8
in plane namespace: 93
in car namespace: 93.75 </span>
说明: 如果没有名空间的干扰,函数重载时选择规则将是非常简单。只要实参是double类型,则调用的是前面的函数;如果实参是int类型,则调用后面的函数。但是由于名空间的参与,就出现了上面的运行结果。所以在编程的时候一定要注意名空间对函数重载的影响。 应注意:调用函数时,如果实参和形参的数据类型实在没有办法完全匹配,可能会对实参进行适当的数据类型转换。比如,将char类型转换为int类型,或进一步将int类型转换为double类型。这种是将数据类型从简单往复杂转换,一般不会丢失信息。另外一种转换是反过来,将double类型转换为int类型,或进一步将int类型转换为char类型。这种是将数据类型从复杂往简单转换,可能会丢失部分信息。在调用函数的时候,不同的情况下,C++对上述两种转换的优先级是不同的。当引入了名空间后,则参与了上述优先级顺序的分配。
struct是namespace的很好的近似,但实际上还是相差很远。它在很多方面很欠缺,其中很明显的一点是 对运算符的处理。如果运算符被定义为 结构的静态成员,它就只能通过函数调用来使用,而不能象常规的运算符所设计的那样,可以通过自然的语法来使用:
// 定义一个模拟名字空间的结构,结构内部包含widgets的类型
// 和函数。widgets对象支持operator+进行加法运算
struct widgets {
class widget { ... };
static const widgetoperator+(const widget& lhs,constwidget& rhs);
...
};
// 为上面所述的widge和operator+ 建立全局(无修饰符的)名称
typedef widgets::widget widget;
const widget (* const operator+)(constwidget&, // 错误!
constwidget&); // operator+不能是指针名
widget w1, w2, sum;
sum = w1 +w2; // 错误! 本空间没有声明
// 参数为widgets 的operator+
sum = widgets::operator+(w1,w2); // 合法, 但不是"自然"的语法
对于类型名,可以用类型定义(typedef)来显式地去掉空间引用。例如,假设结构s(模拟的名字空间)内有个类型名t,可以这样用typedef来使得t成为s::t的同义词:
typedef sdm::handle handle;
正因为这些限制,所以一旦编译器支持,就要尽早使用真正的名字空间。
在原有定义好的名空间的基础上,随时可以往里增加成员。
一 :<iostream>和<iostream.h>格式不一样,前者没有后缀,实际上,在你的编译器include文件夹里面可以看到,二者是两个文件,打开文件就会发现,里面的代码是不一样的。后缀为.h的头文件c++标准已经明确提出不支持了,早些的实现将标准库功能定义在全局空间里,声明在带.h后缀的头文件里,c++标准为了和C区别开,也为了正确使用命名空间,规定头文件不使用后缀.h。 因此,当使用<iostream.h>时,相当于在c中调用库函数,使用的是全局命名空间,也就是早期的c++实现;当使用<iostream>的时候,该头文件没有定义全局命名空间,必须使用namespace std;这样才能正确使用cout。
二: 由于namespace的概念,使用C++标准程序库的任何标识符时,可以有三种选择:
1、直接指定标识符。例如std::ostream而不是ostream。完整语句如下:
std::cout << std::hex << 3.4 << std::endl;
2、使用using关键字。
using std::cout; using std::endl; using std::cin;
以上程序可以写成
cout << std::hex << 3.4 << endl;
3、最方便的就是使用
using namespace std;
例如: using namespace std;这样命名空间std内定义的所有标识符都有效(曝光)。就好像它们被声明为全局变量一样。那么以上语句可以如下写: cout <<hex << 3.4 << endl;因为标准库非常的庞大,所以程序员在选择的类的名称或函数名时就很有可能和标准库中的某个名字相同。所以为了避免这种情况所造成的名字冲突,
就把标准库中的一切都被放在名字空间std中 。但这又会带来了一个新问题。无数 原有的C++代码都依赖于使用了多年的伪标准库中的功能 ,他们都是在 全局空间 下的。所以就有了<iostream.h>和<iostream>等等这样的头文件,一个是为了兼容以前的C++代码,一个是为了支持新的标准。 命名空间std封装的是标准程序库的名称 ,标准程序库为了和以前的头文件区别,一般不加".h"