2. 名称空间声明区域(declaration region):可以进行声明的区域。
全局变量的声明区是整个文件,函数内声明的变量的声明区是代码块。
潜在作用域(potential scope):声明位置开始到声明区截止。
作用域(scope):变量对程序的可见范围。
变量的作用域是潜在作用域的子集。
例如,在函数中声明的局部变量会隐藏同名的全局变量,导致全局变量的作用域缺少一部分。
名称空间(namespace):每个声明区都可以声明名称,并且这些名称独立于其他声明区的名称。(例如:一个函数中的局部变量不会和另一个函数的局部变量发生冲突。)所以说,每个声明区就是一个名称空间。
C++允许我们自己创造名称空间。为了统一概念,我们不再把代码块当成名称空间,所以名称空间只有两种:全局名称空间 用户创造的名称空间
我们创造两个名称空间Jack和Jill
int Hill;
namespace Jack{
double pail;
void fetch();
int pal;
struct well{...};
}
namespace Jill{
double bucket(double n);
double fetch;
int pal;
struct Hill{...};
}
2.1 名称空间的法则
- 名称空间只有两种:全局名称空间 用户创造的名称空间
- 全局名称空间是最基本的名称空间是。全局名称空间就是整个文件。对于其他的名称空间来说,它们存在的位置是:只能嵌套在另一个名称空间中,但是不能位于代码块中。
换句话说,名称空间要么是全局名称空间,要么就是嵌套在另一个名称空间中,但是不能位于代码块中。- 任何名称空间中的名称不会和其他名称空间中的名称发生冲突,例如,Jack中的fetch不会和Jill中的fetch发生冲突,Jill中的Hill不会和全局变量Hill发生冲突。
- 默认情况下,名称空间中的名称具有外部链接性和静态持续性。当然了,在全局名称空间中,我们有局部变量、全局变量、静态局部变量(内部链接性的静态变量),那么我们也可以在用户创造的名称空间中设置相应的变量。如果你对这些名词不熟悉可以看看我写的这篇博客。内存模型
- 用户创造的名称空间的一切声明和定义法则和全局名称空间一样。例如,我们也可以像全局名称空间那样,给jack名称空间,添加变量,添加函数定义,等等所有操作
namespace Jack{
int extra;
}
namespace Jack{
void fetch(){
std::cout<<"hellon";
}
}
2.2 语法细谈 2.2.1 using声明那么,既然名称空间这么多,我们如何在一个名称空间中使用其他名称空间中的名称呢?作用域解析符——::,正如上面这段代码中所示,我们使用了std空间中的cout名称。
#includenamespace Jill{ double bucket(double n); double fetch=0; int pal; } double fetch=2.222; void foo(); int main(){ using Jill::fetch; foo(); std::cout<<"in main fetch : "< in foo fetch : 1.111 in main fetch : 1.111 global fetch : 2.222这里的using Jill::fetch;就是我们所谓的 using声明,他使得main函数中的一切fetch名称都视为是Jill::fetch。这里的fetch会覆盖同名的全局变量,如果我们想要调用全局变量,只需要添加作用域解析符,::fetch就是全局变量,我们还验证了一个法则,默认情况下,Jill名称空间中的名称的持续性都是静态的,因为在foo函数中对fetch的修改,会影响main函数中的fetch的值。
#includenamespace Jill{ double bucket(double n); double fetch=0; int pal; } using Jill::fetch; void foo(); int main(){ foo(); std::cout<<"in main fetch : "< 2.2.2 using编译指令上面这段代码是正确的,我们把using声明放到了全局名称空间中,这样子全局名称空间的main函数中使用的fetch就是Jill::fetch。
#includeusing namespace std; int main(){ cout<<"hello world!n"; } 上面这段代码就是最简单的C++程序,这里的#include
会把头文件iostream放到std名称空间中,using namespace std;就是所谓的 using编译指令,上面那段代码会使得 std名称空间的所有名称在全局名称空间中可用。 #includeint main(){ using namespace std; cout<<"hello world!n"; } 有的程序员喜欢上面这段代码,因为他使得 std名称空间中的所有名称在main()函数中可用,这种写法更加安全。
- using声明使一个名称可用,而using编译指令使得所有的名称都可用。
- using编译指令增加了名称冲突的可能性
namespace Jack{ double pail; void fetch(); int pal; struct well{...}; } namespace Jill{ double bucket(double n); double fetch; int pal; struct Hill{...}; } using namespace Jack; using namespace Jill;2.2.4 using声明和using编译指令的比较上面这段代码会报错,因为他会将Jack和Jill中的所有名称导出到全局名称空间,从而引发fetch和pal名称的冲突。
假设名称空间和声明区域定义了相同的名称。如果使用using声明,将名称空间中的名称导入声明区域,会导致两个名称发生冲突,从而报错。如果使用using编译指令将名称空间中的所有名称导入声明区域,不会报错也不会发出警告,因为局部名称会隐藏名称空间中的名称。
#includenamespace Jill{ double bucket(double n); double fetch=0; int pal; } double fetch=1.11; void foo(); int main(){ using namespace Jill; char fetch[10]="hello"; std::cout< hello 0 1.11 in foo fetch : 1.11仔细看上面这段代码,很神奇是不是,它不会报错,这里有3个fetch:全局fetch,Jill名称空间中的fetch和局部变量fetch,在mian函数中使用using namespace Jill;不会报错,因为这里局部名称会隐藏名称空间名称和全局名称。如果把using namespace Jill;改成using Jill::fetch就会报错。
- using声明像是声明了一个名称,它会与同名的局部名称发生冲突。
- using编译指令更像是名称解析,它导入大量的名称,如果存在同名局部名称,则局部名称会覆盖名称空间名称。
2.2.5 名称空间的嵌套using声明比using编译指令更安全
namespace Jill{ double bucket(double n); double fetch=0; int pal; } namespace elements{ namespace fire{ int flame; } } namespace myth{ using namespace elements; using std::cin; using std::cout; using Jill::fetch; }名称空间中仍然可以创造名称空间,使用using声明和using编译指令。
using namespace myth; cin>>fetch; cin>>fire::flame;2.2.6 未命名的名称空间而且using编译指令具有传递性,所以using namespace myth;也会把elements名称空间中的所有名称导入全局名称空间。
所以cin>>fire::flame;也是合法的。
也可以给名称空间创造别名
namespace favorite=elements::fire;
using favorite::flame;未命名的名称空间中的变量是静态全局变量的替代品;
#includestatic int count; void foo(); int main(){ using namespace std; foo(); cout<<"now :"< now :1 now :2 now :3上面这段代码等价于下面这段代码:
#includenamespace{ int count; } void foo(); int main(){ using namespace std; foo(); cout<<"now :"< 3. 一些建议namespace{int count;}就好像后面跟着一条using编译指令一样,由于名称空间没有名字,所以在其他文件中使用using编译指令或者using声明。
名称空间的诞生是因为大型编程项目难于管理。如果你只写一些简单的程序请忽视下列建议。
4.一个例子
- 使用已命名的名称空间中声明的变量代替全局变量
- 使用未命名的名称空间中声明的变量代替静态全局变量
- 将函数库类库放在一个名称空间中
- 少用using编译指令
- 避免在头文件中使用using编译指令。
- 导入名称时,首选using声明和作用域解析符
- 首选在局部使用using声明
//名称空间6.h #include#include using std::cin; using std::cout; using std::endl; using std::string; namespace pers{ struct Person { string fname; 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); } //名称空间6.cpp #include"名称空间6.h" namespace pers{ void getPerson(Person &rp){ cout<<"enter first name: "; cin>>rp.fname; cout<<"enter last name: "; cin>>rp.lname; } void showPerson(const Person& rp){ cout<>rd.amount; } void showDebt(const Debt & rd){ showPerson(rd.name); cout<<": $"< //名称空间6main.cpp #include"名称空间6.h" void other(); void another(); int main(){ using debts::Debt; using debts::showDebt; Debt golf={{"Benny","Goatsniff"},120.0}; showDebt(golf); other(); another(); } void other(){ using namespace debts; Person dg={"Doodles","Glister"}; showPerson(dg); cout< PS D:studyc++> g++ -I .path_to_c++include -o test .path_to_c++名称空间6main.cpp .path_to_c++名称空间6.cpp PS D:studyc++> .test.exe Benny Goatsniff: $120 Doodles Glister enter first name: Ann enter last name: Cherry enter debt: 200 enter first name: Ban enter last name: Tank enter debt: 230 enter first name: Sam enter last name: Captain enter debt: 300 Ann Cherry: $200 Ban Tank: $230 Sam Captain: $300 Total debt: $730 Milo Rightshift



