关于C++ namespace,我们需要回答如下几个问题:
1、什么是namespace?
2、为什么有namespace?也就是namespace的作用
3、namespace的使用方法有哪些?
(一)什么是namespace?
首先我们看看C++标准规范中对namespace的定义如下:
named-namespace-definition:
namespace identifier { namespace-body }
unnamed-namespace-definition:
namespace { namespace-body }
namespace包含两种,一种是有名的命名空间,一种是无名的命名空间。我们先看如下两个实例:
- //有名的namespace的使用方法
- #include <iostream>
- namespace named_namespace{
- //变量
- int value = 1;
- //函数
- void Output() { std::cout << value << std::endl;}
- //结构体
- struct Test{
- int value;
- void Output() {std::cout << value << std::endl;}
- };
- //类
- class Object{
- public:
- void SetValue(const int& val){ value_ = val; }
- void Output() {std::cout << value_ << std::endl;}
- private:
- int value_;
- };
- }
上面是有名的命名空间的使用方式,可以看出来,命名空间可以包含变量、函数、结构体以及类等,统统将他们包含进来。
上述是无命名空间的使用方式。只不过无命名空间是没有名字的。由于没有名字,所以其它文件无法引用,它只能在本文件的作用域内有效,它的作用域使重无名命名空间声明开始到本文件结束。在本文件使用无名命名空间成员时不必用命名空间限定。其实无名命名空间和static是同样的道理,都是只在本文件内有效,无法被其它文件引用。
(二)为什么有namespace?也就是namespace的作用
第一部分,我们讲述了namespace的具体定义形式,但是我们为什么要使用namespace呢?namespace能给我们实际变成过程中带来哪些好处呢?
namespace主要用于解决“命名冲突”问题。在大型项目开发中,可能有很多人同时进行编写代码,不同的模块可能使用了相同的全局变量,当系统集成的时候,就会发生命名冲突的问题。我们通过如下简单代码可以明白什么是命名冲突:
头文件head1.h定义如下:
- int value = 1;
头文件head2.h定义如下:
- double value = 2.2;
主函数定义如下:
- #include <iostream>
- #include "head1.h"
- #include "head2.h"
- int main(int argc, char** argv)
- {
- std::cout << value << std::endl;
- return 0;
- }
编译错误信息如下:
从上面代码可以看出,value出现在两个头文件中进行定义了,主函数main中value发生了冲突,不能确定是哪一个头文件中的。上述只是一个简单的示例,当面对成百上千的头文件时候,很难避免相同名字的出现。
此时,namespace的作用就显现出来了,namespace是“命名空间”,从名字上就可以看出来,就是构成了一个空间,在空间里面的内容和空间外的内容无关,只和本空间内部的信息有关。简单点讲就是,namespace用于分割项目整个空间,使每一个空间相对独立,虽然相同的名字存在,但是他们处于不同的namespace中,就不会发生命名冲突。
上述发生错误的代码可以通过namespace进行解决:
头文件head1.h定义如下:
- namespace head1{
- int value = 1;
- }
头文件head2.h定义如下:
- namespace head2{
- double value = 2.2;
- }
主函数定义如下:
- #include <iostream>
- #include "head1.h"
- #include "head2.h"
- int main(int argc, char** argv)
- {
- std::cout << head1::value << std::endl;
- std::cout << head2::value << std::endl;
- return 0;
- }
运行结果如下:1 和 2.2
上面着重讲述了有名namespace的作用。
其实无名namespace的作用也是避免“命名冲突”。关于无命名空间,需要注意如下两点:
1、无名名字空间主要是保持代码的局部性
2、在C++编译器实现时,无名名字空间其实是有名字的,这个隐含的名字跟它所在编译单元名字相关。所以基于这一点,我们不能跨编译单元使用无名名字空间中的名字
namespace的使用方法有哪些
(二)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
#include
#include
using namespace std;
这样命名空间std内定义的所有标识符都有效。就好像它们被声明为全局变量一样。那么以上语句可以如下写:
cout << hex << 3.4 << endl;
因为标准库非常的庞大,所程序员在选择的类的名称或函数名时就很有可能和标准库中的某个名字相同。所以为了避免这种情况所造成的名字冲突,就把标准库中的一切都被放在名字空间std中。但这又会带来了一个新问题。无数原有的C++代码都依赖于使用了多年的伪标准库中的功能,他们都是在全局空间下的。
所以就有了和等等这样的头文件,一个是为了兼容以前的C++代码,一个是为了支持新的标准。
命名空间std封装的是标准程序库的名称,标准程序库为了和以前的头文件区别,一般不加".h"
今天用了VISUAL C++写了个小程序(VS2005),很简单很简单的,但是就是编译不通过出现一个奇怪的问题:错误 1 errorC2668: “max”: 对重载函数的调用不明确
最初代码如下
#include using namespace std; template T max (T a,T b) { return ((a>b)?a:b); } void main() { double x,y; cin>>x>>y; cout<<"Max number is "<<(max(x,y))< cin>>x; } |
我将这段代码放到VC++ 6.0下竟然通过了,程序运行也正常。这让我百思不得其解。后来终于弄明白了!
其实在std命名空间下还有一个MAX函数,而且实现的功能也是一样的……我昏。利用转到定义功能可以看到微软是怎么写MAX函数的。这里为了不被鄙视就不贴微软的代码了。
明白了为什么出现这个错误我们就改写代码如下:
#include using std::cin; using std::cout; using std::endl; template T max (T a,T b) { return ((a>b)?a:b); } int main() { double x,y; cin>>x>>y; cout<<"Max number is "<<(max(x,y))< cin>>x; } |
这是我比较推荐的做法,因为C++ PRIMER, EFFECTIVEC++上都是用这种方式的,但是谭浩强的书上都是一句using namespacestd;就搞定,我觉得蛮简洁的就一直用了,没想到带来那么多的问题,以前在友元函数上还碰到莫名的错误呢。
我们作个总结性的复习吧。
(A)这简单的程序里只有全局和局部空间,没有其它的名字空间:
void main( ) {
std::cout << ""; // 错误:std不存在。
cout << ""; // 错误:std不存在cout就不用提了。
}
(B)这个则多了一个叫std的名字空间。std这名字也可见了:
#include<iostream>
void main( ) {
std::cout << ""; // OK:可通过std骚扰cout。
cout << ""; // 错误:cout仍然被包在std里面,不可见。
}
iostream头文件里有std的声明。在编译之前,
预处理器会用iostream里的全部内容来代替
“#include<iostream>”这个预处理器指示符。
效果是,这程序包含iostream里的所有代码。
include是包含的意思。
包含指示是在全局空间里,所以在指示之后的任何地方,
std这个名字是可见、可访问的。注意是std这个名字而
不是std内的变量。
(C)这个不包含声明std的代码却尝试打开std包,
落了个跟(A)一样狼狈的下场:
using namespace std;
void main( ) {
std::cout << ""; // 错误:std不存在。
cout << ""; // 错误:std不存在cout就不用提了。
}
(D)这个声明了std(曝露了std)并把std打开,
让里面的名字(cin、cout、cerr、...)跟
std一样暴露。
[相信你闭上眼都能写出这段代码了]
注意:我们加上#include相当于把namespace std{cin,out}这个命令空间整体包含进来,如果直接使用cin或者cout,相当于没有在程序中声明,所以可以通过上述三种方法进行声明。因此include是需要的,同时上述三种声明方式也必须要。