1.1 namespace 引入
看代码,看结果
// namespace.cpp
#include <iostream>
namespace Department_X {
static int id = 99;
void fool(void)
{
std::cout << "departmentX" << std::endl;
}
}
namespace Department_Y {
static int id = 88;
void fool(void)
{
std::cout << "departmentY" << std::endl;
}
}
int main()
{
/*打印出部门X所有的id变量*/
std::cout << Department_X::id << std::endl;
/*使用部门X所有的fool函数*/
Department_X::fool();
/*打印出部门Y当中的id变量*/
std::cout << Department_Y::id << std::endl;
/*使用部门Y所有的fool函数*/
Department_Y::fool();
return 0;
}
输出如下:
99
departmentX
88
departmentY
对于名字空间(英文名 namespace ),我们可以这样理解:
名字空间A里有自己的函数或变量
名字空间B里有自己的函数或变量
名字空间A和B中的函数或者变量可以相互重名,不冲突
如果我们想要访问名字空间的数据的话,最直接的方法是指出名字空间,形如:
#名字空间std中的cout、endl
std::cout
std::endl
#名字空间Department_X中的变量id,函数fool
Department_X::id
Department_X::fool()
1.2 namepace 调用方式优化
喜欢便捷的读者,可能会提出这样一个疑问:
每次使用名字空间里的变量或者函数,我都要不厌其烦的在前面写一个 空间名字::函数名,假如我要访问数百个名字空间中的函数,我就要写数百个名字空间名!!!多么可怕
C++ 提供了另一种简洁的方式,使用形式using namespace xxx的格式,将影响名字空间xxx中的函数或变量的可见性
// namespace.cpp
#include <iostream>
/*在文件作用域中被引入可见性,后面所有的函数都不必调用类似std::cout,std::endl 啦*/
using namespace std;
namespace Department_X {
static int id = 99;
void fool(void)
{
cout << "departmentX" << endl;
}
}
namespace Department_Y {
static int id = 88;
void fool(void)
{
cout << "departmentY" << endl;
}
}
/*函数bar1 引入Department_X的可见性*/
void bar1(void)
{
using namespace Department_X;
cout << "bar1 ";
/*自然是部门X中的那个fool()函数咯*/
fool();
}
/*在函数bar2 引入Department_Y的可见性*/
void bar2(void)
{
using namespace Department_Y;
cout << "bar2 ";
/*自然是部门Y中的那个fool()函数咯*/
fool();
}
int main()
{
bar1();
bar2();
return 0;
}
1.3 再探名字空间:名字隐藏
// namespace.cpp
#include <iostream>
using namespace std;
namespace Department_X {
static int id = 99;
void fool(void)
{
cout << "departmentX" << endl;
}
}
void bar1(void)
{
using namespace Department_X;
cout << "bar1 ";
/*读者会发现,此时的id是部门X中的那个*/
cout << "id = " << id << " &id = " << &id << endl;
/*此时又定义了一个新的id,它是一个栈地址,为什么不再是部门X中的那一个了呢?
解释就是using namespace xxx 只做到将名字空间中的符号可见性给调用者,但是并不是把变量给生硬的引用在这里*/
int id = 299;
cout << "id = " << id << " &id = "<< &id << endl;
}
int main()
{
bar1();
return 0;
}
为了理解我们还有下面的编程风格(变量引入到作用域,而非可见性)
// namespace.cpp
#include <iostream>
using namespace std;
namespace Department_X {
static int id = 99;
void fool(void)
{
cout << "departmentX" << endl;
}
}
void bar1(void)
{
//这样就是真的把部门X中的变量id引用弄到这个函数里啦!!!
using Department_X::id;
cout << "bar1 ";
cout << "id = " << id << " &id = " << &id << endl;
}
int main()
{
bar1();
return 0;
}
那么考虑下面的代码,它其实不能编译通过,为什么呢?重复声明/定义!!!现在,引入可见性和声明的区别是否更加清晰了呢?
// namespace.cpp
#include <iostream>
using namespace std;
namespace Department_X {
static int id = 99;
void fool(void)
{
cout << "departmentX" << endl;
}
}
void bar1(void)
{
using Department_X::id;
cout << "bar1 ";
cout << "id = " << id << " &id = " << &id << endl;
int id = 299;
//cout << "id = " << id << " &id = "<< &id << endl;
}
int main()
{
bar1();
return 0;
}
1.4 名字空间分类
C++语法规定:
全局或者全局类型统一位于匿名空间
用户通过 namespace 定义有名空间的变量/类型
参考如下代码
#include <iostream>
using namespace std;
namespace SpecName
{
void foo()
{
cout << "User Namespace::foo" << endl;
}
};
void foo()
{
cout << "Ni Ming namespace" << endl;
}
int main()
{
//using namespace SpecName;
SpecName::foo();// 有名空间的函数访问
::foo(); //全局/匿名空间的访问
return 0;
}
1.5 namespace 实现机制
我将尝试说明namespace 在C++语言层面的实现机制
// file1 namespace_02.h
#ifndef __NAMESPACE_02__H
#define __NAMESPACE_02__H
namespace Department_X {
extern int id;
void fool(void);
};
namespace Department_Y {
extern int id;
void fool(void);
};
#endif // __NAMESPACE_02__H
/****************************************/
//file2 namespace_02.cpp
#include <iostream>
namespace Department_X {
int id = 99;
void fool(void)
{
std::cout << "departmentX id = " << id << std::endl;
}
}
namespace Department_Y {
int id = 88;
void fool(void)
{
std::cout << "departmentY id =" << id << std::endl;
}
}
/***************************/
//file3 main.cpp
#include <iostream>
#include "namespace_02.h"
void show_dep1(void)
{
using namespace Department_X;
fool();
std::cout << id;
}
void show_dep2(void)
{
using namespace Department_Y;
fool();
std::cout <<id;
}
int main()
{
show_dep1();
show_dep2();
return 0;
}
将上面的三个文件一起编译 连接生成可执行文件 a.out
在ubuntu 系统中,使用GNU提供的工具集
nm a.out
你会看到符号表有这几个东西
0000000000601068 D _ZN12Department_X2idE
000000000040093a T _ZN12Department_X4foolEv
000000000060106c D _ZN12Department_Y2idE
0000000000400977 T _ZN12Department_Y4foolEv
名字空间参与了符号修饰,如果读者对编译/链接 有所了解的话,很容易猜测出namespace的原理…有兴趣的话可以翻看一本书《程序员的自我修养》