命名空间,using声明,指令与作用域,重载与namespace
基础与特性
命名空间是组织代码的方式,当大型程序,总是会包含很多库,那么命名空间可以防止名字冲突(多重定义)
namespace name { …… }
//
作用域:
命名空间是可以非连续的,比如分离的接口和实现文件,我们定义同名命名空间,甚至在一个文件多次定义,将不会发生任何链接错误
因为如果之前没有定义命名空间名,将产生新的定义,而如果之前定义过,将打开原本名字作用域,继续向内部添加成员
但同时注意,命名空间名必须在定义的作用域内保持唯一,也就是说,不可以嵌套同名的命名空间名
注意:通常不要将include放在namespace内部,因为可能会让std命名空间包含在自定义的命名空间中,将引发错误
//
访问说明:
如果在同一命名空间内,不必使用namespace::访问前缀,
而如果我们想在外部定义属于命名空间的成员,需要明确指出使用的命名空间namespace::
对于模板的特例化版本,必须定义在原始模板所属的命名空间中,当然我们只要在这个命名空间声明特例化,就可以在外部定义,
//
全局命名空间:所有全局作用域的名字都隐式添加到全局命名空间中,全局命名空间是隐式的,没有名字,所以访问形式为::member
嵌套命名空间:遵守内外作用域规则(内部隐藏外部,名字查找(由内到外,向上查找是否有声明))
内联命名空间:inline出现在namespace前,且为首次定义时,一种特殊的嵌套命名空间,内部名字可以被外部命名空间直接使用
未命名命名空间:
- 内部变量拥有静态生命周期,相当于没有定义命名空间,
- 没有连续特性,因此多次定义未命名命名空间,不会产生错误,并且属于不同实体,
- 我们不需要::访问它,成员名字的作用域和命名空间的作用域等价,因此要避免名字冲突
别名
直接使用命名空间非常繁琐,我们可以通过其他方式简化,
namespace 别名 = 命名空间名(可以为嵌套命名空间)
using声明
using name::member
可以出现在全局作用域,局部作用域或者命名空间作用域中,类中的using声明局限于使用其基类中定义的名字;
一个using声明一次只能引入一个命名空间成员
由using引入的名字作用域(相当于定义了别名):从using声明点开始,直到包含该using声明的作用域结尾
遵守内外作用域规则,如果出现同名将引发错误
using指令
using namespace name;
可以出现在全局作用域,局部作用域或者命名空间作用域中
using指示使得特定命名空间的所有名字可见,
由using引入的名字作用域:using指示具有将命名空间成员提升到包含命名空间本身和using指示的最近作用域的效果
表示名字作用域为包含namespace定义的作用域,并且仅限制具有using声明的作用域访问
实例:
#include <iostream>
#include "named_namespace.h"
using namespace std;
int i = 200; //全局变量
int main()
{
using namespace name_17_2_3;
cout << "i=" << i << endl; //产生歧义,名称空间的i,和全局变量i
return 0;
注意:尽量不要再头文件包含全局位置的using声明或using指示,因为头文件不应该包含定义,所以最多在函数或命名空间内部包含
实参相关查找
当调用函数时,如果函数传递类类型对象的实参,除了常规作用域(本身,外部)产找名字(此函数)外,还会查找类所在的命名空间
与友元关系:如果friend声明为友元的函数,外部没有声明,第一次出现在友元声明中,我们认为是最近层空间的成员,
如果这个函数有类类型对象的实参,会查找类所在的命名空间,那么可以找到友元函数,不用声明直接使用
函数重载与namespace
当遇到实参相关查找时:会将常规作用域和类所在的命名空间的所有函数加入到候选函数集中
using声明和using指令::
- 根据名字作用域,将隐藏外部作用域同名函数,
- 如果同一作用域有同名同形参列表函数,将发生错误
- 否则同名但形参列表不同情况,相同或外部作用域下,作为重载函数,加入到候选函数集中