一、using声明与using编译指令简述
在使用名称空间中名称时,不希望每次使用都对名称进行限定(如 A::Pail),能直接使用名称即可,针对这种情况,C++提供了两种机制:using声明与using编译指令。
using声明: 使特定标识符可用。
using编译指令: 使整个名称空间可用,即整个名称空间中的名称可用。
二、使用
2.1 using声明使用方式
using声明: 由被限定的名称和它前面的关键字using组成,如下格式:
using A::Pail // 一个using声明
using声明是将特定的名称添加到使用using声明所属的声明区域中。例如,main()函数中使用using声明A::Pail将Pail添加到main()函数定义的声明区域中(只在main()函数中使用)。如下代码所示:
// 名称空间A
namespace A
{
double bucket(double n)
{
// 代码块
...
}
double fetch;
struct Hill
{
// 结构体数据
...
}
}
// 全局变量
char fetch;
int main()
{
using A::fetch; // 将A名称空间中的fetch名称添加到main函数中
double fetch; // 错误,此声明区域已声明fetch
cin >> fetch; // 使用using声明的A名称空间中的fetch,覆盖同名的全局变量
cin >> ::fetch; // 使用全局变量fetch,因为使用了作用域解析运算符
}
举例: 在函数外使用using 声明时,将把名称添加到全局名称空间中。
void other();
namespace Jill
{
double bucket(double n)
{
// 代码块
...
}
double fetch;
struct Hill
{
// 结构体数据
...
};
}
// 将Jill名称空间中的fetch添加到全局名称空间中
using Jill::fetch;
int main()
{
cin >> fetch; // 使用Jill空间中的fetch,读取一个值赋值给fetch
other();
}
void other()
{
cout >> fetch; // 显示Jill名称空间中fetch的值
}
2.2 using编译指令使用方式
using编译指令: using声明使一个名称可用,而using编译指令使名称空间中所有名称可用,不需要使用作用域解析运算符。using编译指令由名称空间名和关键字using namespace组成。格式如下:
using namespace typename;
在全局声明区域中使用using编译指令,将使该名称空间中的所有名称全局可用,如下:
#include <iostream>
using namespace std;
在函数中使用using编译指令,将使名称空间中的名称在该函数中可用,如下:
int main()
{
using namespace jack;
...
}
2.3 注意事项
(1)编译器不允许同时使用using声明不同名称空间的相同名称,因为这将导致二义性;举例如下:
using jack::pal;
using jill::pal;
pal = 4; // 此处使用出现问题,不知道使用的是哪一个,存在二义性
三、using声明与using编译指令比较
3.1 比较
(1)使用using编译指令导入一个名称空间中所有的名称与使用多个using声明是不一样的,而更像是大量使用作用域解析运算符。
(2)使用using编译指令,将进行名称解析,就像在包含using声明与名称空间本身的最小声明区域中声明了名称一样。
(3)名称空间是全局的,如果使用using编译指令导入一个已经在函数中声明的名称,则局部名称将会隐藏名称空间下的同名名称,就像局部名称隐藏同名的全局名称一样。
(4)使用using声明时,就好像声明了相应的名称。如果某个名称已经在函数中声明,则不能使用using声明导入相同名称,会引起名称冲突。
示例:
namespace Jill
{
double bucket(double n){...}
double fetch;
struct Hill{...};
}
char fetch; // 全局名称空间中
int main()
{
using namespace Jill; // using编译指令引入名称空间中全部名称
Hill Thrill; // 创建一个Jill::Hill类型结构体
double water = bucket(2); // 使用Jill::bucket
double fetch; // 这不是一个错误,它会隐藏Jill::fetch
cin >> fetch; // 输入一个值赋给局部fetch
cin >> ::fetch; // 输入一个值赋给全局fetch
cin >> Jill::fetch; // 输入一个值赋给Jill::fetch
}
int foom()
{
Hill top; // 错误,无using编译指令,也没有using声明
Jill::Hill crest; // 正确,直接使用限定名称
}
注:
(1)main函数中的using编译指令将名称空间的名称视为在函数之外声明,但它不会使得该文件中的其他函数能够使用这些名称。因此foom函数中不能使用未限定的标识符Hill。
(2)假设名称空间和声明区域定义了相同的名称,如果试图使用using声明将名称空间的名称导入该声明区域,则两个名称会发生冲突。如果使用using编译指令将该名称空间的名称导入该声明区域,则局部版本将隐藏名称空间版本。
3.2 安全方面
一般情况,使用using声明要比使用using编译指令更安全。
(1)using声明只导入指定的名称,如果该名称与局部名称冲突,则编译器将发出指示
(2)using编译指令导入所有的名称,包括可能并不需要的名称,如果与局部名称冲突,则局部名称将覆盖名称空间中的名称,而编译器并不会发出警告。
(3)using编译指令使得名称空间开放,意味着名称空间的名称可能分散在多个地方,这使得难以准确知道添加了哪些名称。
四、名称空间特性之using声明与using编译指令
4.1 嵌套式名称空间
(1)可以将名称空间声明进行嵌套:
namespace elements
{
namespace fire
{
int flame; // 这里的flame使用方式elements::fire::flame或者using namespace
// elements::fire
...
}
float water;
}
(2)可以将上述名称空间的using编译指令和名称的using声明放在一个名称空间下,示例:
namespace myth
{
using Jill::fetch;
using namespace elements;
using std::cout;
using std::cin;
}
A: 假设访问Jill::fetch,此时Jill::fetch位于名称空间myth(在此名称空间,也称为fetch),可这样访问:
std::cin >> myth::fetch
B: 由于fetch也位于Jill名称空间,因此仍然可以称为Jill::fetch,也可以这样访问:
std::cin >> Jill::fetch
C: 如果没有与之冲突的局部变量,也可以这样:
using namespace myth;
std::cin >> fetch
(3)现在考虑将using编译指令用于myth名称空间的情况。using编译指令是可传递的。如果A op B且B op C,则A op C,则说操作op是可传递的。例如,>运算符是可传递的,如果A > B且B > C,则A > C。
名称空间示例如下:
using namespace myth;
等同于如下两条指令:
using namespace myth;
using namespace elements;
(4)可以给名称空间创建别名,例如有如下名称空间:
namespace my_very_favorite_things { … }
则可以使用如下语句让mvft称为my_very_favorite_things 的别名:
namespace mvft = my_very_favorite_things ;
可以使用这种技术来简化对嵌套名称空间的使用:
namespace MEF = myth::elements::fire;
using MEF::flame;
4.2 未命名的名称空间
可以通过省略名称空间的名称来创建未命名的名称空间:
namespace
{
int ice;
int bandycoot;
}
说明:
(1)这就像后面跟着using编译指令一样,也就是说,在该名称空间声明的名称的潜在作用域为:
从声明点到该声明区域末尾。从这个方面看,它们与全局变量相似。
(2)由于名称空间没有名称,因此不能显示地使用using编译指令和using声明来使它在其它位置可用。具体的说,不能在未命名名称空间所属文件之外的其它文件中使用该名称空间的名称。
(3)提供了链接性为内部的静态变量的替代品。
示例:
static int count = 1; // 静态变量
int other();
int main()
{
...
}
int other()
{
count = 10;
}
// 未命名名称空间替代方式
namespace
{
int count;
}
int other();
int main()
{
...
}
int other()
{
count = 10;
}