所谓namespace,是指标识符的各种可见范围。C++标准程序库中的所有标识符都被定义于一个名为std的namespace中。
<iostream>和<iostream.h>是不一样,前者没有后缀,实际上,在你的编译器include文件夹里面可以看到,二者是两个文件,打开文件就会发现,里面的代码是不一样的。后缀为.h的头文件c++标准已经明确提出不支持了,早些的实现将标准库功能定义在全局空间里,声明在带.h后缀的头文件里,c++标准为了和C区别开,也为了正确使用命名空间,规定头文件不使用后缀.h。因此,当使用<iostream.h>时,相当于在c中调用库函数,使用的是全局命名空间,也就是早期的c++实现;当使用<iostream>的时候,该头文件没有定义全局命名空间,必须使用namespaces td;这样才能正确使用cout。
由于namespace的概念,使用C++标准程序库的任何标识符时,可以有三种选择:
1、直接指定标识符。例如std::ostream而不是ostream。完整语句如下:
std::cout << std::hex<< 3.4<< std::endl;
2、使用using关键字。
using std::cout;
using std::endl;
以上程序可以写成
cout << std::hex<< 3.4<< endl;
3、最方便的就是使用using namespace std;
例如:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
这样命名空间std内定义的所有标识符都有效(曝光)。就好像它们被声明为全局变量一样。那么以上语句可以如下写:
cout << hex<< 3.4<< endl;
因为标准库非常的庞大,所程序员在选择的类的名称或函数名时就很有可能和标准库中的某个名字相同。所以为了避免这种情况所造成的名字冲突,就把标准库中的一切都被放在名字空间std中。但这又会带来了一个新问题。无数原有的C++代码都依赖于使用了多年的伪标准库中的功能,他们都是在全局空间下的。所以就有了<iostream.h>和<iostream>等等这样的头文件,一个是为了兼容以前的C++代码,一个是为了支持新的标准。命名空间std封装的是标准程序库的名称,标准程序库为了和以前的头文件区别,一般不加".h"
下面通过例程说明关键字namespace的用法。
#include <iostream> using namespace std; 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; }
运行结果:
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 202 93
从上面可以看出,名空间定义了一组变量和函数,它们具有相同的作用范围。对于不同的名空间,可以定义相同的变量名或函数名,在使用的时候,只要在变量名或函数名前区分开不同的名空间就可以了,
名空间可以被嵌套定义,使用时要逐级对成员用名空间限定符::来引用。
系统默认有一个全局名空间,它包含了所有的外部变量。这个命名空间没有名字,引用这个名空间里的变量时要使用名空间限定符::,前面没有名字。在不使用命名空间的情况下,我们知道,不可以在不同文件中定义相同名字的外部变量,这是因为它们属于同一个全局名空间,名字不可以重复。
可以给名空间取一个别名。一般别名是一个比较短的名字,来简化编程。
在原有定义好的名空间的基础上,随时可以往里增加成员。
在前面的例程中可以看到,为了使用时的方便,又引入了关键字using。利用using声明可以在引用名空间成员时不必使用名空间限定符::。此外,关键字namespace和using的使用,对函数重载有一定的影响。
#include <iostream> using namespace std; 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); }
运行结果:
in car namespace: 3
in car namespace: 3.8
in plane namespace: 93
in car namespace: 93.75
说明:如果没有名空间的干扰,函数重载时选择规则将是非常简单。只要实参是double类型,则调用的是前面的函数;如果实参是int类型,则调用后面的函数。但是由于名空间的参与,就出现了上面的运行结果。所以在编程的时候一定要注意名空间对函数重载的影响。
最后,举一个例子,头文件h1.h里面包含命名空间ns,h1.cpp,h2.cpp,main.cpp三个编译单元分别包含该头文件,此时 h1.h里面包含命名空间ns的定义方式就要格外注意,否则引起重复定义错误。
1、h1.h
namespace ns { extern int i; void f(void); class A { public: A(); int print(void); private: int m_data; }; }
2、h1.cpp版本一
#include<iostream> #include "h1.h" using namespace std; using namespace ns; int ns::i = 10; void ns::f(void) { cout << "ns::f" << endl; } int ns::A::print(void) { cout << m_data << endl; return m_data; } ns::A::A() { m_data = 100; }
2、h1.cpp版本二
#include<iostream> #include "h1.h" using namespace std; namespace ns { int i = 10; void f(void) { cout << "ns::f" << endl; } int A::print(void) { cout << m_data << endl; return m_data; } A::A() { m_data = 100; } }
3、h2.cpp
#include<iostream> #include "h1.h" using namespace std; using namespace ns; void h(void) { i = 90; A a; a.print(); cout << "h" << endl; }
4、main.cpp
#include<iostream> #include "h1.h" using namespace std; using namespace ns; int main(void) { i = 2; f(); A a; a.print(); return 0; }
运行结果:
ns::f
100