C++学习 十二、内存模型,作用域,名称空间 (3)命名空间
前言
本篇继续C++学习记录,命名空间。
如果,某程序使用的两个开源库都定义了一个名为start()的函数,那么在main()函数中该如何调用某个库的start()函数呢?
C++通过命名空间被用于限制命名的作用域。
命名空间namespace
创建命名空间
关键字namespace
用于创造一个命名空间,示例如下:
namespace mySpace1{
int a;
int b;
void func(int);
}
namespace mySpace2{
int a;
int b;
void func(int);
}
命名空间中可以存放变量,函数,结构,类,#define,typedef等等。不同命名空间的同名符号不会出现冲突。
可以随时把名称加入到一个命名空间中,在命名空间中的函数可以分开声明和定义:
namespace mySpace1{
int d;
void func(int x){
cout << x << endl;
}
}
注意:命名空间必须是全局的,或者被嵌套在另一个命名空间中。不能在函数中创建命名空间。
访问命名空间内成员
访问命名空间内的成员时,需要使用域名解析运算符::
,示例如下:
void main(){
mySpace1::a = 1;
mySpace1::func(a);
}
全局命名空间
除了自己定义的命名空间外,文件本身自带了一个全局命名空间,全局定义的变量,函数,结构等都属于该文件的全局命名空间,可以通过::Name
来调用全局名称,示例如下:
int varName = 1;
void func(){
int varName = 2;
cout << varName << endl; // 2
cout << ::varName << endl; // 1
}
嵌套命名空间
可以在命名空间中创建命名空间:
namespace space1{
namespace space2{
int b;
}
int a;
}
分别使用space1::space2::b;
,space1::a
来使用变量。
命名空间别名
嵌套命名空间名字较长,可以给命名空间取别名:
void main(){
namespace ss12 = space1::space2;
ss12::a = 1;
}
注意:可以在函数中给命名空间取别名,但不能在函数中创建新命名空间。
无名命名空间
也可以定义没有名字的命名空间,示例如下:
namespace {
int a;
int b;
void func(int);
}
无名命名空间因为没有名字,不能在其他文件中使用该空间中的名称。因此,无名namespace中的名称具有类似内部链接的性质,可以作为静态全局变量和静态函数的替代品。
关键字using
域名解析运算符::
的使用比较繁琐(虽然我还是比较习惯直接用::
),可以使用using
关键字来进行简化。
using声明
using声明用于把命名空间中的名称添加到全局或者局部,示例如下:
namespace sp{
int a;
int b;
}
using sp::b;
void main(){
using sp::a;
// int a; //error
a = 1;
int b;
b = 2;
::b = 3;
}
上面这一段代码中,使用using
声明后,就可以不指明命名空间解析sp::
来调用命名空间中的a
和b
。
局部using
声明将sp::a
添加到main
函数的局部代码块中,因此不能再在该局部声明同名变量了,否则将报error: redeclaration of 'int a'
。
全局using
声明将sp::b
添加到全局,main
函数局部仍然可以声明一个同名变量,同时隐藏全局变量。
using编译指令
using编译指令的使用方法为using namespace spacename
,使得命名空间中所有名称都在编译指令的位置区域可用,最常见的用法是使用标准命名空间std
:
void main(){
using namespace std;
}
然后就能够直接使用std命名空间中的名称。
using声明与using编译指令
上面提到,using声明更类似于在全局或者局部声明了一个变量,因此在该位置不能再定义同名变量。
而using编译指令则不同,它更类似于使用作用域解析:
namespace space{
int a;
int b;
}
int b;
void main(){
using namespace space;
a = 1; // space::a
// b = 1; // error!
space::b = 1; // space::b
::b = 2; // global b;
int b;
b = 3; // local b
}
上面的示例中,using编译指令使得space
中的名称a
可以直接在main()
函数中使用。
对于b
,由于定义了全局版本和命名空间版本,因此在main()
函数中,直接使用名称b
将报error: ambiguous reference 'b'
,也就是编译器分不清到底使用的是全局名称还是空间中的名称。
最后,使用using编译指令时,main()
中还能再定义了一个局部b
,使得全局、局部、局部空间版本的b
共存。而使用using声明时,局部空间版本的b
与局部b
不能共存。
这也就是为什么说,using声明类似全局或局部声明,而using编译指令类似作用域解析。
注意:尽量直接使用作用域解析来使用命名空间里的名称。
命名空间规范
下面是一些命名空间使用的通用规范:
- 尽量使用命名空间中的变量来替代全局变量、静态全局变量
- 类库、函数库应当放在命名空间中
- 不在头文件中使用using namespace指令
- 尽量使用作用域解析方法使用空间内的名称
后记
本篇三章内容把C++的内存模型,作用域和命名空间记录完。
下篇就要进入C++的特色核心,类与对象。