C++ 自定义namespace的使用技巧
的有关信息介绍如下:
C++给命名冲突引入了namespace(命名空间)概念,从而降低了各种第三方库间的标识符冲突问题。那么如何定义自己的namespace以及如何实现及使用呢?
所谓分离式实现就是指“声明”和“定义”分别保存在不同的文件中,一般讲声明保存在.h文件、定义保存在.cpp文件中。如此放置便可达到分离式方式。
那么将声明和定义分离有什么意义吗?首先从非分离式(声明的同时给出定义)看,其内容一般保存在.h文件中,以供多个源文件引用。但是将定义放在头文件,那么当多个源文件使用#include命令包含此类的头文件便会在链接阶段出现“multiple definition”链接错误!那么想让多个文件使用此头文件,又不引发链接的“multiple definition”错误该怎么办呢?
分离式的实现便可以解决这个问题。因为.h文件中只包含声明,即使被多个源文件引用也不会导致“multiple definition”链接错误。所以分离式实现增强了命名空间的实用性。以下举例说明。(2015-04-11 更新)
//Two.h 定义
#ifndef TWO_H
#define TWO_H
#include
using namespace std;
namespace MyNameSpace
{
void Say();
}
namespace MyPrintSpace
{
void Say();
}
#endif
//Two.cpp 实现
#include
#include
#include "Two.h"
using namespace std;
void MyNameSpace::Say()
{
cout << "MyNameSpace::Say()" << endl;
}
void MyPrintSpace::Say()
{
cout << "MyPrintSpace::Say()" << endl;
}
#include "Two.h"预编译指令只引入了namespace的定义到当前文件中,此时还必须使用‘namespace_name::Identifier’的方式访问名称。
1.若想直接使用某个标识符,应该使用如下方法引入标识符:
using 命名空间标识符::标识符;
2.将namespace下面的所有标识符引入,可使用:
using namespace 命名空间标识符;
将自定义的namespace的实现和定义分别放在.cpp和.h文件中,达到分离式实现。Two.h文件用于命名空间的声明,Two.cpp文件用于命名空间中函数、类等的实现。
// One.cpp 测试文件
#include
#include
#include "Two.h"
using namespace std;
using namespace MyNameSpace;
using namespace MyPrintSpace;
void Say()
{
cout << "Galobel::NameSpace" << endl;
}
int main()
{
::Say(); //全局命名空间::
MyNameSpace::Say();
MyPrintSpace::Say();
return 0;
}
#编写Makefile文件
CC=g++
OBJS=One.o Two.o
NameSpace : ${OBJS}
${CC} -o NameSpace ${OBJS}
One.o : One.cpp Two.h
${CC} -c -o One.o One.cpp
Two.o : Two.cpp Two.h
${CC} -c -o Two.o Two.cpp
clean:
rm NameSpace One.o Two.o
#Terminal中编译
$ make
g++ -c -o One.o One.cpp
g++ -c -o Two.o Two.cpp
g++ -o NameSpace One.o Two.o
运行结果:
$ ./NameSpace
Galobel::NameSpace
MyNameSpace::Say()
MyPrintSpace::Say()
using声明/using编译指令
using std::cin; // using声明使特定的标识符可用
using namespace std; // using编译指令使整个名称空间可用
using编译指令和using声明的比较
使用using编译指令导入一个名称空间中所有的名称与使用多个using声明是不一样的,而更像是大量使用作用域解析运算符。使用using声明时,就好像声明了相应的名称一样。
如果某个名称已经在函数中声明了,则不能用using声明导入相同的名称。然而,使用using编译指令时,将进行名称解析,就像在包含using声明和名称空间本身小声明区域中声明了名称一样。
如果使用using执行导入一个已经声明的名称,则局部名称将隐藏名称空间名,就像隐藏同名的全局变量一样。
不过仍然可以像下面的示例那样使用作用域解析运算符:
namespace Jill
{
double buket(double n) { ... }
double fetch;
struct Hill { ... };
}
char fetch; // gloabl namespace,全局空间
int main( )
{
using namespace Jill; // 导入命名空间所有名称
Hill Thrill;
double water = bucktet(2); // use Jill:bucket( )
double fetch; // 隐藏Jill::fetch
cin >> fetch; // local fetch
cin >> ::fetch; // 全局fetch
cin >> Jill::fetch; // Jill::fetch
}
int foom( )
{
Hill top; //错误
Jill::Hill crest; //valid
}
//
假设名称空间和声明区域定义了相同的名称。如果试图使用using声明将名称空间的名称导入该声明区域,则这两个名称会发生冲突,从而出错。如果是会用using编译指令将该名称空间的名称导入该声明区域,则局部版本将隐藏名称空间版本。
//////////////////////////////////////////////////////////
注:以上内容摘录自《C++ Primer Plus(第6版)中文版》第9章 内存模型和名称空间 第328页
一般说来,使用using声明比较使用using编译指令更安全,这是由于它(using declaration)只导入指定的名称。如果该名称与局部名称发生冲突,编译器将发出指示。using编译指令导入所有名称,包括可能不需要的名称。如果与局部名称发生冲突,则局部名称将覆盖空间版本,而编译器并不会发出警告。另外,名称空间的开放性意味着名称空间的名称可能分散在多个地方,这使得难以准确知道添加了哪些名称。
// in file One.h
namespace seek
{
void Func();
}
//////////// end One.h /////////
// in file Two.h
namespace seek
{
void Add(double a, double b);
}
//////////// end Two.h /////////
//
在文件One.h和Two.h中都有命名空间seek的声明,这种写法是正确的。
2015-04-02 第一次更新
2015-04-11 第二次更新



