名称空间只是用来对名称进行管理的一种方式,首先要了解一下传统C++名称空间。
1.声明区域:声明区域是指可以在其中声明的区域,例如可以在函数外部声明全局变量(区域为所在的文件),在函数块内声明局部变量(区域为所在的块),其声明区域就为其声明位置。
2.潜在作用域:变量的潜在作用域从声明开始,到其声明区域的结尾结束。但是潜在作用域比声明区域小,因为变量必须定义后才能使用。
然而,变量并不是在潜在作用域就是可见的,它可能被其他嵌套的声明区域隐藏,例如:
int a = 0;
int main(){
//同理
int a = 1;
{
//在此声明区域中,则会隐藏其他变量
int a = 2;
}
return 0;
}
在上面中,每个不同区域的同名变量会隐藏其他的同名变量,变量对于程序的可见性称为作用域。
而现在的名称空间则是C++为了解决名称冲突的问题提供的空间工具。
C++关于全局变量和局部变量的规则定义了一种名称空间层次,每个声明区域都可以声明名称,这些名称独立于其他声明区域中声明的名称。在一个函数中声明的局部变量不会和在另一个函数中声明的局部变量冲突。
新的名称空间)
C++新增这样一种功能,通过定义一种新的声明区域来创建命名的名称空间,这样做是为了提供一个声明名称的区域。一个名称空间中的名称不会和另外一个空间中相同的名称冲突。
namespace Jill {
double bucket(double n) {
//statments
}
double fetch;
struct Hill {
int num;
char gendet;
};
}
namespace Jack {
double bucket(double n) {
}
double fetch;
struct Hill
{
int a;
double b;
};
}
默认情况下,名称空间中声明的名称的链接性是外部的(除非引用了常量).名称空间是开放的,可以把名称添加到已有的名称空间中。
Jack::pail = 12.32;
Jill::pail = 23.32;
通过::(作用域解析运算符)来使用该空间对应的限定名。
未被修饰的名称成为未限定的名称,包含名称空间的名称称为限定名称。
同时可以使用using 声明和using 编译指令来添加名称到声明区域,例如:
//如果在外部使用,则将名称添加到全局声明区域中
using namespace Jill;
int main() {
//using声明将特定的名称添加到它所属的声明区域中
//通过声明区域,将对应名称添加到对应名称空间中
using Jill::bucket;
//为Jill中的bucket
bucket = 1;
return 0;
}
当只使用using声明时,using Jill::bucket只会将Jill空间中的bucket加载到对应的main块中的声明区域中,而如果在全局声明区域声明则可以在整个文件中使用。
当使用using 编译命令时,using namespace Jill会将其空间中所有变量添加到对应声明区域。
使用以上两种方法都可以避免使用::作用域解析运算符来声明变量,而直接使用变量名即可,但是有一种情况下必须使用解析运算符。
//正确,因为都是不同的标识符,内存单元不同
jack::pal = 3;
jill::pal = 1;
//错误,会产生二义性,编译器不知道使用哪个
using jack::pal;
using jill::pal;
pal = 10; //which one?now have a conflict(用哪个?发生冲突)
同时还需要注意using声明和using编译的不同:
假设名称空间和声明区域定义了相同的名称,如果试图使用using声明将名称空间的名称导入该声明区域,则这两个名称会发生冲突,从而报错.
int foom(){
Hill top; //ERROR
Jill::Hill crest; //valid
}
如果使用using编译指令将该名称空间的名称导入该声明区域,则局部版本将隐藏名称空间版本.
int foom(){
using namespace Jill;
Hill top;
Hill crest; //use local value
}
一般来说使用using声明比using编译指令安全,因为它导入指定名称,当与局部名称发生冲突时,编译器将发出警告。,而使用using编译命令如果发生冲突会默认使用局部名称覆盖命名空间名称,不会发出警告。
名称空间支持者希望我们使用using声明的方式去导入名称,而不是使用using编译的方式去导入。
但我们可使用嵌套式名称空间,创建一个包含常用using声明的名称空间。
namspace elements
{
namespace fire
{
int flame;
}
float water;
}
在该嵌套空间中,flame指的是element::fire::flame。同样,可以使用下面的using编译指令使内部名称可用
using namespace element::fire;
也可以在名称空间中使用using编译指令和using声明。
namespace myth
{
using Jill::fetch;
using namespace elements;
using std::cout;
using std::endl;
using std::cin;
}
假设要访问Jill::fetch。由于Jill::fetch位于myth中,因此可以这样访问:
std::cin >> myth:fetch;
也可以使用Jill::fetch;来进行访问。
接下来使用using编译命令来用于myth名称空间情况。
using命令是可传递的,若A ->B,B->C,则A->C是可行传递的。
using namespace mythl; == (using namespace myth; using namespace elements;)
同时也可以给名称空间创建别名.
namespace my_favorite_things{....};
则可用下面语句设置别名
namespace mvft = my_favorite_things;
或者
namespace MEF = myth::elements::fire;
int main(){
using MEF::flam;
flam = 2;
}
未命名的名称空间
可以通过省略名称空间的名称来创建未命名的空间。
namespace{
int ice;
int bandycoot;
}
名称空间示例
namesp.h文件
#ifndef NAME_SP_
#define NAME_SP_
#include<string>
namespace pers {
//个人名称结构
struct Person
{
std::string fname;
std::string lname;
};
//设置个人名称信息
void getPerson(Person&);
//查看名称信息
void showPerson(const Person&);
}
namespace debts {
//可传递
using namespace pers;
struct Debt
{
//名称结构
Person name;
//金额
double amount;
};
void getDebt(Debt&);
void showDebt(const Debt&);
double sumDebts(const Debt ar[], int n);
}
#endif // !NAME_SP_
namesp.cpp文件
#include<iostream>
#include"namesp.h"
namespace pers {
using std::cout;
using std::cin;
void getPerson(Person& rp) {
cout << "Enter first name: ";
cin >> rp.fname;
cout << "Enter last name: ";
cin >> rp.lname;
}
void showPerson(const Person& rp) {
cout << rp.lname << "," << rp.fname;
}
}
//债务命名空间
namespace debts {
//设置债务情况
void getDebt(Debt& rd) {
getPerson(rd.name);
std::cout << "Enter debt: ";
std::cin >> rd.amount;
}
//显示名称和消费金额
void showDebt(const Debt& rd) {
showPerson(rd.name);
std::cout << ": $" << rd.amount << std::endl;
}
//计算金额总和
double sumDebts(const Debt ar[], int n) {
double total = 0;
for (int i = 0; i < n; i++)
{
total += ar[i].amount;
}
return total;
}
}
main程序文件
#include<iostream>
#include"namesp.h"
void other(void);
void another(void);
int main() {
using debts::Debt;
using debts::showDebt;
//创建一个债务结构
Debt golf = { {"Benny","Goatsniff"},125.2 };
//查看对应债务情况
showDebt(golf);
other();
another();
return 0;
}
void other() {
using std::cout;
using std::endl;
using namespace debts;
//动态创建一个Person结构对象
Person* dg = new Person{ "Doodles","Glister" };
showPerson(*dg);
delete dg;
cout << endl;
//创建一个Debt结构的数组
Debt zippy[3];
int i;
for (i = 0; i < 3; i++)
getDebt(zippy[i]);
for (i = 0; i < 3; i++)
showDebt(zippy[i]);
//显示总计债务金额
cout << "Total debt: $" << sumDebts(zippy, 3) << endl;
return;
}
void another() {
using pers::Person;
Person person = { "Milo","Rightshift" };
pers::showPerson(person);
std::cout << std::endl;
}
在头文件namesp.h中,定义了两个命名空间,其中pers中定义了一个结构体Person用来存放用户的名和姓。还提供了两个函数原型,用于设置姓名和打印姓名。
另一边使用namesp.cpp实现了命名空间中函数原型的定义,将原型的定义放入其中。
最后在main函数中通过头文件引入对应的命名空间,同时使用using声明和using编译命令将命名空间中的结构体和函数引入,同时使用。
至此我们可以有一个初步对头文件和命名空间的认识了,我们将在头文件中定义命名空间,声明对应空间中的变量信息,同时可以定义函数原型(类似于接口),然后通过同名源文件去实现命名空间中的函数定义,或者定义外部变量,最后在main文件中引入头文件,使用using声明或者using namespace编译命令来使用该命名空间中声明的变量和函数。