您的位置首页百科问答

C++ 自定义namespace的使用技巧

C++ 自定义namespace的使用技巧

的有关信息介绍如下:

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 第二次更新