理解 C++名称空间(namespace)
文章目录
在看《C++ Primer Plus》第 9 章名称空间时,一些个人的理解,如有错误欢迎指出。
-
声明区域:传统的C++中,声明区域有如下几种:在函数外面声明的变量,其声明区域为所在文件;在函数内声明的变量,其声明区域为其所在的代码块;
-
名称空间(namespace):个人理解是一种特殊的声明区域,他是一种人为划定出来的名称空间,使用起来更加灵活,它看起来像下面这样。
namespace Jack { double fetch; int sum(int a, int b) { return a + b; }; }
有三种方式使用它:
方式1 使用作用域解析运算符
::
如下面这个例子:
namespace Jack { double fetch; } int main() { Jack::fetch = 3; cout << Jack::fetch << endl; return 0; }
输出:
3
方式2
using <名称空间的名字>::<名称>
直接把名称空间里的声明,添加过来。
namespace Jack { double fetch; } int main() { using Jack::fetch; int fetch = 0; // 这条语句会报错!因为fetch已经定义了 }
上面这个例子有点类似下面这种情况,即重复的声明了fetch
int main() { double fetch = 1; int fetch = 0; // 这条语句会报错!因为fetch已经定义了 }
但绝对不是等同的,即
using Jack::fetch
并不是定义了一个局部变量,而只是使用了Jack中的fetch。namespace Jack { int fetch; } void change_fetch() { ++Jack::fetch; } int main() { using Jack::fetch; fetch = 1; cout << fetch << endl; change_fetch(); cout << fetch << endl; return 0; }
输出:
1 2
再比如下面这个,函数声明的例子。
namespace Jack { int sum(int a, int b) { return a + b; }; } int main() { using Jack::sum; cout << sum(1,2) << endl; cout << Jack::sum(1,2) << endl; // 这种用法总是对的,不管前面用了using namespace还是using Jack::sum return 0; }
下面这段代码也是对的,注意看和上面例子之间的区别和联系
int main() { int sum(int a, int b); cout << sum(1,2) << endl; return 0; } int sum(int a, int b) { return a + b; };
对比上面两个例子,可以看出,
using Jack::sum
,把函数声明添加了进来,虽然说namespace里有写函数的具体代码,但显然using Jack::sum
只添加了声明,而没添加代码。下面这个代码是错的,因为函数里不能再定义一个函数:int main() { int sum(int a, int b) { // 函数里不能再定义函数 return a + b; }; cout << sum(1,2) << endl; return 0; }
方式3
using namespace <名称空间的名字>
只考虑
namespace Jack
在函数外面的情况。那么using namespace Jack
可以让名称空间Jack
里的名称,可以被「看到」并使用,不需要再使用::
了。但具体取决于这条语句在函数外还是函数里。
如果
using namespace Jack
在函数外面,那么整个文件都可以看到并直接使用Jack
里的名称了(这就和在函数外声明的全局变量或者函数很类似了)如果
using namespace Jack
在函数里,那就是该函数内部里可以「看到」并使用Jack
里的声明,这里说是代码块,但整个文件也可以看作代码块嘛,比如在main
中使用Jack
里的声明:namespace Jack { double fetch; } int main() { using namespace Jack; fetch = 2; // Jack名称空间里的fetch cout << fetch << endl; int fetch = 3; // 这条语句不会报错哦,局部的int fetch会隐藏掉Jack的double fetch,注意是隐藏,不是覆盖 cout << fetch << endl; // 打印出局部的int fetch的值 cout << Jack::fetch << endl; // Jack::fetch依旧存在,输出可以发现仍然是2! return 0; }
输出:
2 3 2
上面这个例子,和下面这个很相似,
double fetch; int main() { fetch = 2; cout << fetch << endl; int fetch = 3; // 这条语句不会报错哦,局部的int fetch会隐藏掉全局的double fetch,注意是隐藏,不是覆盖 cout << fetch << endl; // 打印出局部的int fetch的值 cout << ::fetch << endl; // 全局的fetch依旧存在,输出可以发现仍然是2! return 0; }
输出:
2 3 2
上面的例子中,在
main
函数外面,定义了一个全局变量fetch
,然后同样在main
内部又定义了一个int
类型的fetch
,这样全局变量就被「隐藏」,通过作用域解析运算符::
,仍然可以访问到全局变量,可以发现它依旧在那里。在函数外面的
namespace Jack
,此时就好像普通全局变量和其他函数声明一样,只不过它们被放到namespace
中了。再看一个例子:
char fetch; namespace Jack { int fetch; } int main() { using namespace Jack; // 使用了using namespace Jack,再想访问fetch就必须得用::fetch了,因为直接用fetch被认为是Jack里的fetch Jack::fetch = 1; // Jack的fetch ::fetch = 'a'; // 如果没有using namespace Jack, 这里就不用::也可以了 double fetch = 2.1; // 局部的double fetch fetch = 0.1; // 局部的double fetch cout << Jack::fetch << endl; // Jack里的fetch cout << ::fetch << endl; // 全局的char fetch cout << fetch << endl; return 0; }
输出:
1 a 0.1
这个例子里有三个
fetch
,全局的char fetch
,namespace Jack的int fetch
,还有main中定义的局部变量double fetch
,在这种情况下,fetch
仅指代局部的double fetch
,::fetch
指代全局的char fetch
,Jack::fetch
指代namespace Jack的int fetch
。而在下面这个例子中,只能使用
::
了。fetch将不知道指代谁,从而报错:char fetch; namespace Jack { int fetch; } int main() { using namespace Jack; // 使用了using namespace Jack,再想访问fetch就必须得用::fetch了,因为直接用fetch被认为是Jack里的fetch Jack::fetch = 1; // 正确,Jack的fetch ::fetch = 'a'; // 正确,如果没有using namespace Jack, 这里就不用::也可以了 fetch = 0.1; // 错误!不清楚是哪个fetch,因为全局的char fetch和Jack的fetch冲突了 return 0; }
只有去掉char fetch的声明,或者去掉using namespace Jack,直接使用
fetch
,才会有明确的指向。