C++名称空间
名称空间
名称空间可以是全局的,也可以位于另一个名称空间中,但不能位于代码块中。因此,在默认情况下,在名称空间中声明的名称的链接性为外部的(除非它引用了常量)
名称空间是开放的,你可以在名称空间中追加内容
namespace geo{double x;double y;void func(); //函数原型
}
namespace geo{double z; // z 也会放入名称空间void func(){std::cout << "func \n" ;} // 你可以在这里实现函数原型
}
注意不能在名称空间完成赋值操作,如果想初始化则必须写成初始化的形式
namespace geo{double x =100; //可以初始化x = 1.5; // 不能赋值,这条语句将会报错
}
你也可以将函数的声明和定义写在一起,就不用提供原型
namespace geo{
double add(double x,double y){return x+y;
}
}
也可以分文件进行编写,将原型写到头文件里
// head.h
#pragma once
namespace geo{double add(double,double);
}
// main.cpp
#include <iostream>
#include "head.h"
double geo::add(double x,double y){return x+y;
}
int main()
{std::cout << geo::add(1.0,3.0) << '\n'; return 0;
}
你可以看到和类的定义写法差不多,可以分文件进行编写
在局部变量隐藏了全局变量的时候,如何调用全局变量?
#include <iostream>
int x = 100;
int main()
{using namespace std;int x =10;cout << "local var x = " << x << " &x = " << &x <<endl;cout << "global var x = " << ::x << " &x = " << &::x <<endl; return 0;
}
默认全局变量都会处于全局名称空间,你可以通过::
操作符来获取全局变量,如果你自己定义了一个全局名称空间例如geo
你也可以通过 ::geo::x
来访问geo
里面的变量,但是这没什么意义,你本可以直接用geo::x
,不过从这里你可以看出geo
是属于全局名称空间的一部分,可能你会想起前面提到的,名称空间定义是全局的或者位于另一个名称空间里。
你可以利用namespace
给名称空间取别名
namespace geox = geo;
这句话会让geox
成为geo
的别名,这种用法在存在名称空间嵌套的时候比较要用例如
namespace geo{namespace line{int x;}}namespace geox = geo::line; //这会让geox变为geo::line的别名
using
关键字
using
声明,你可以使用using namespace::var
来进行使用var
,例如经常使用的using std::cout
,std
为标准名称空间using
编译指令,这会将名称空间的所有名称都可用,例如using namespace std
,这样你就能尽情使用标准名称空间的内容
这两者有什么区别?
using
声明就是真实的声明,你使用了之后就不能声明相同的名字的变量,例如如下的代码不能通过编译
#include <iostream>namespace geo{int x;}int main(){using geo::x;double x; // 出现声明冲突 return 0;}
而using
编译指令是允许你定义重名的变量,这会隐藏掉名称空间里面的变量
#include <iostream>
namespace geo{int x;
}
int main()
{using namespace geo;double x=100;std::cout << "x = "<< x << " &x = " << &x <<std::endl;std::cout << "geo::x = "<< geo::x << " &x = " << &geo::x <<std::endl; // 覆盖后还想调用则使用geo:: return 0;
}
如果有两个名称空间有同名变量会发生什么?
#include <iostream>namespace geo{int x=0;}namespace geox{int x =1;}int main(){using namespace geo;std::cout << "x = "<< x << " &x = " << &x <<std::endl;using namespace geox;std::cout << "x = "<< x << " &x = " << &x <<std::endl;return 0; }
答案是这无法通过编译,会产生模糊定义,编译器不知道选择哪个x
(即使你后用的using
编译指令也无法覆盖前面的定义)
如果混用using
声明会发生什么?
#include <iostream>
namespace geo{int x=0;
}
namespace geox{int x =1;
}
int main()
{using geo::x;std::cout << "x = "<< x << " &x = " << &x <<std::endl; using namespace geox;std::cout << "x = "<< x << " &x = " << &x <<std::endl;return 0;
}
答案是两个x
均是0
,因为using
声明的行为就是声明,他会覆盖掉using
编译的结果,即使编译命令在后面
如果都使用using
声明可以吗?
#include <iostream>namespace geo{int x=0;}namespace geox{int x =1;}int main(){using geo::x;std::cout << "x = "<< x << " &x = " << &x <<std::endl;using geox::x;std::cout << "x = "<< x << " &x = " << &x <<std::endl; return 0; }
想必你已经想到了结果,因为using
声明就是声明!这会产生重定义,而不能通过编译
其他特性
可以在名称空间中使用using
关键字
namespace geo{int y = 1;int x =100;namespace line{int x=0;}
}
namespace geox{using namespace geo;using geo::line::x;int y =100;
}
我这里面其实有很多问题,先要说明的是using
编译指令具有传递性
使用
using namespace geox;
相当于
using namespace geox;
using namespace geo;
注意没有 using geo::line::x
因为这是声明而不是编译指令
所以
using namespace geox;
std::cout << x ; //模糊的定义
因为geox
中有一个x
,geo
中也有一个x
这和之前提到的两个名称空间具有相同的名称是一样的
接下来分析一下geox::x
的值是多少?
前面说过声明会覆盖using
编译指令,所以geox
中的x
实际上是geo::line::x
的别名,所以值为0
同样
using namespace geox;
std::cout << y ; //模糊的定义
这里就很好理解了,geox
包含了自己定义的y
,geo
也一样和前面的情况完全一致。
接下来注意名称空间中使用using namespace geo
并不意味着geox
包含了geo
,而是相当于把,geo
里面的名称都导入了geox
,这可以被覆盖,我们前面诸多例子说明了这个现象,所以你其实不能用geox::geo
,但是你可以用geox::line
这与geo::line
完全相同,可以理解为别名
最后看一种更为复杂的情况
#include <iostream>namespace geo{namespace line{int x=0;}}namespace geox{using namespace geo;namespace line{int y=10;}}int main(){using namespace geox;using namespace std;cout << line::x; // 模糊的定义 return 0;}
这会编译出错,因为无法确定使用哪一个line
,因为即使你使用using name geo
让geox
包含了geo
的内容,可能你以为这可以利用namespace
的开放原则,可以将int y=10;
添加到geo
中,但实际上不行,编译器只会看到geox
自己定义的line
,由于前面提到的using 传递原则
所以会出现模糊的定义,因为有两个line
都可见,这与前面提到的两个名称空间包含相同的名称类似,并且你没有办法通过geox
访问到geo
中的内容,这一点很有意思
匿名namespace
实现文件作用域的静态变量替代方案,使得变量具有内部链接,别的文件不可见
namespace {int x; // x是静态变量 ,具有内部链接性
}
这个效果于static int x;
但是匿名namespace
的好处更多
- 可以分组对一批内容设定内部链接性
- 支持模板,类/结构 ,
static
无法修饰这些