目录
三、名称空间
在C++中,名称可以是变量、函数、结构、枚举、类以及类和结构的成员。随着项目的增大,名称间的相互冲突可能性增加。例如某两个产商定义了均包含List,Tree类的库,若用希望使用一个库的List类以及另外一个库的Tree,则需要使用C++提供的名称空间工具。
1、传统C++的名称空间
先介绍一下术语:
①声明区域:指可以在其中声明的区域,例如全局变量,声明区域为所在的文件,函数中声明的变量,声明区域为函数所在的代码块
②潜在作用域:变量的潜在作用域从声明点开始,到其声明区域的结尾。也就是变量需要在定义后才可以使用。
但是并非所有的变量在其潜在区域都是可见的,它可能在某个代码块中被隐藏(例如上篇所说的全局变量和局部变量变量名称相同的情况)
此外,C++还定义了一种空间名称层次,每个声明区域都可以声明变量,这些名称独立于在其他声明区域中声明的名称。在一个函数声明的变量不会与另一个函数中声明的变量产生冲突。
下图对声明区域、潜在作用域和作用域进行了说明
2、新的名称空间特性
(1)创建名称空间
C++定义一种新的声明区域来创建命名的名称空间,目的是提供一个声明名称的区域。一个名称空间的名称与另一个名称空间的名称不会因同名而冲突。同时允许程序的其他部分使用该名称空间中声明的东西。下面代码使用新的关键字namespace创建了两个名称空间:jack和Jill。
namespace Jack {
double pail;
void fetch();
int pal;
struct Well {
....
};
}
namespace Jill {
double bucket(double n) {}
double fetch;
int pal;
struct Hill {...};
}
名称空间可以是全局的,也可以位于另一个名称空间中,但不能位于代码块。因而,默认情况下,名称空间中声明的名称的链接性为外部(除非它引用常量)
全局名称空间对应文件级声明区域,因而全局变量现在被描述为位于全局名称空间中。
名称空间是开放的,可以把名称加入到已有的名称空间中
namespace Jill
{
char * goose(const char * );
}
另外,前面Jack名称空间声明了fetch()函数原型,则可以在文件后面(或者另外一个文件中)再次使用jack名称空间来提供该函数的代码。
namespace Jack
{
void fetch()
{
...
}
}
(2)使用名称空间
可以使用作用域解析运算符::来访问给定名称空间中的名称。
Jack::pail = 13.23;
Jill:Hill mole;
Jack::fetch();
也可以通过using声明和using编译指令,下面再详细讲述using声明和using编译
using Jill::fetch; //using声明,将特定的名称添加到它所属的声明区域中,之后使用fetch替代Jill::fetch。
using namespace Jill; //unsing编译,可使用Jill名称空间里的所有名称。
(3)using声明和using编译指令
C++提供了两种机制(using声明和using编译指令)来简化对名称空间的使用,using声明使得特定的标识符可用,using编译指令使得整个名称空间可用。
#include <iostream>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
namespace Jill
{
double bucket(double n){.....}
double fetch;
struct Hill{......};
}
char fetch;
int main(int argc, char** argv) {
using Jill::fecth;
double fetch;//不可以使用,fetch已经用于代替Jill::fecth
cin>>fetch;//将输入赋给名称空间中的fecth
cin>>::fetch;//将输入赋给全局变量fecth
return 0;
}
通过using声明using Jill::fetch;可以使用fetch代替Jill::fetch。上述例子将using放在函数内,将和局部变量一样,覆盖全局变量。而将using声明放在函数外,将把名称添加到全局名称空间中。
using编译指令使得所有的名称都可用,由关键字using namespace +名称空间名构成 ,在函数内使用,则定义为全局可用;在函数内使用,则定义为局部可用
另外,使用using声明和using编译指令增加了名称冲突的可能性
jack::pal=3;
jill::pal=4;//可以使用
using jack::pal;
using jill::pal;
pal=4;//不允许,此时不知道pal属于jack还是jill
(4)using编译指令和using声明比较
使用using声明像声明了相应的名称,如果函数中以及有了某个名称的声明,则不能用using声明导入相同的名称。
使用using编译会进行名称解析,就像在包含using声明和名称空间中的最小声明区域声明的名称一样。如果使用using编译导入名称空间,则局部名称将隐藏名称空间名。
即在名称冲突时,两者有部分差异,例如,名称空降和using声明的区域存在相同的名称,如果在该区域中使用using声明导入名称,则两个名称会发生冲突而出错。另外,如果使用using编译指令,则该区域的局部版本名称将会隐藏名称空间的版本。
因而使用using声明相对using编译指令会更安全。
(5)名称空间的其他特性
可以将名称空间声明进行嵌套
namespace elements
{
namespace fire
{
int flame;
...
}
float water;
}
elements::fire::flame
elements::water;
using namespace elements::fire;使得内部名称可用
可以在名称空间使用using编译指令和using声明
namespace myth
{
using Jill:fetch;
using name elements;
using std::cout;
using std::cout;
}
std::cin>>myth::fetch;
std::cin>>Jill::fetch;这两种均可以使用名称的fetch
using namespace myth;//若无与之冲突的名称变量,也可以这样使用
cin>>fetch;
using编译指令是可以传递的,如果A op B且B op C,则A op C。
(6)未命名的名称空间
可以通过省略名称空间来创建未命名的名称空间:
namespace
{
int ice;
int bandycoot;
}
这种无命名的名称空间作用域从声明点到该声明区域末尾。
无法显示使用using编译指令或者using声明,因此不能在未命名名称空间所属文件之外的其他文件中使用该名称空间中的名称。即提供了链接性为内部的静态变量的替代品
namespace
{
int couts;
}
int main
{
}
//等价
static int counts
int main
{
}
3、名称空间及其前途
指导原则:
①使用在已命名的名称空间中声明的变量,而不是使用外部全部变量;
②使用在已命名的名称空间中声明的变量,而不是使用静态全局变量;
③如果开发了一个函数库或者类库,将其放在一个名称空间中。C++当前提倡将标准函数库放在名称空间std中。
④仅将编译指令using作为一种将旧代码转换为使用名称空间的权益之计。
⑤不要在头文件中使用using编译指令。
⑥导入名称时,应=首先使用作用域解析运算符或者using声明方法。
⑦对于using声明,首选将其作用域设置为局部而非全局。
老式头文件iostream.h没有使用名称空间,但新头文件iostream使用了std名称空间
C++的学习笔记持续更新中~
要是文章有帮助的话
就点赞收藏关注一下啦!
感谢大家的观看
欢迎大家提出问题并指正~