Namespaces provide a method for preventing name conflicts in large projects.
命名空间的定义
- 命名空间是由关键字 **
namespace
** 和一对花括号{}
组成。 - 可以出现在 全局作用域 中的声明都可以在
namespace
内出现,包括类、变量、函数、模版和其他命名空间。
命名空间的三个特性
可开放性
- 同一个命名空间可在程序不同地方添加声明
- 通常情况下,不能将
#include
放在命名空间内部 - 在命名空间外定义该命名空间中的成员时,需要明确其所属的命名空间
#include <iostream>
namespace apple{
class A{
};
void func_first(){
std::cout << "apple::func_first() " << std::endl;
}
}
namespace apple{
class B{
};
void func_second();
}
void apple::func_second(){
std::cout << "apple::func_second() " << std::endl;
}
int main(){
apple::func_first();
apple::func_second();
return 0;
}
可嵌套性
- 可嵌套性指命名空间中可以嵌套其他的命名空间
#include <iostream>
namespace fruit{
namespace apple{
class A{
};
void func(){
std::cout << "fruit::apple::func() " << std::endl;
}
}
}
int main(){
fruit::apple::func();
return 0;
}
可匿名性
- 匿名命名空间指 关键字
namespace
后紧跟 花括号{}
,无名称的命名空间 - 匿名命名空间的变量拥有 静态生命周期 :在第一次时被创建,直到程序结束才销毁
- 与其他命名空间不同,匿名命名空间仅在特定文件中有效,其作用域不能横跨多个文件
int i;
namespace {
int i;
}
i = 10; // 错误,匿名命名空间中的变量是静态变量,相当于 static i;
// 且 其作用域与namespace 相同,出现二义性
可内联性
- 对于 使用关键字
inline
声明的命名空间称为 内联命名空间 - 内联命名空间可直接被外层命名空间使用
#include <iostream>
inline namespace apple{
class A{
};
void func_first(){
std::cout << "apple::func_first() " << std::endl;
}
}
namespace fruit{
inline namespace apple{
class A{
};
void func(){
std::cout << "fruit::apple::func() " << std::endl;
}
}
}
int main(){
func_first();
fruit::func();
return 0;
}
两种使用命名空间成员的方式
using
声明
using
声明语句一次仅引入命名空间的一个成员- 作用域:从
using
声明开始,到using
声明所在作用域结束为止 - 可出现在 全局作用域、局部作用域、命名空间作用域及类的作用域中
#include <iostream>
namespace apple{
void func(){
std::cout << "apple::func()" << std::endl;
}
int apple_size = 5;
}
namespace orange{
int orange_size = 4;
void compare(){
using apple::apple_size;
std::cout << "orange::size - apple::size = " << orange_size - apple_size << std::endl;
}
}
int main(){
orange::compare();
return 0;
}
using
指示
using
指示将namespace
中的所有变量引入到using
指示所在的所用域- 作用域:从
using
指示开始,到using
指示所在作用域结束为止 using
指示将命名空间的成员提升至包含命名空间本身 和using
指示的 最近作用域 中
namespace A {
int i;
}
namespace B {
int i;
int j;
namespace C {
namespace D {
using namespace A; // 注入所有来自 A 的名称到全局命名空间
int j;
int k;
int a = i; // i 是 B::i,因为 B::i 隐藏 A::i
}
using namespace D; // 注入来自 D 的名称到 C
// 注入来自 A 的名称到全局命名空间
int k = 89; // 声明与用 using 引入者等同的名称 OK
int l = k; // 歧义:C::k 或 D::k
int m = i; // OK:B::i 隐藏 A::i
int n = j; // OK:D::j 隐藏 B::j
}
}
- 在使用 using 指令指名某命名空间后,若该命名空间被扩充并向其添加了额外的成员和/或 using 指令,则这些额外成员和额外的命名空间通过该 using 指令可见(与 using 声明相反)
namespace D {
int d1;
void f(char);
}
using namespace D; // 引入 D::d1、D::f、D::d2、D::f,
// E::e 及 E::f 到全局命名空间!
int d1; // OK:声明时与 D::d1 不冲突
namespace E {
int e;
void f(int);
}
namespace D { // 命名空间扩展
int d2;
using namespace E; // 传递性 using 指令
void f(int);
}
void f() {
d1++; // 错误:歧义:::d1 或 D::d1?
::d1++; // OK
D::d1++; // OK
d2++; // OK,d2 是 D::d2
e++; // OK:e 是 E::e,因为传递性 using
f(1); // 错误:歧义:D::f(int) 或 E::f(int)?
f('a'); // OK:仅有的 f(char) 是 D::f(char)
}
注:在任何命名空间作用域中的 using 指令
using namespace std;
,将命名空间std
中的所有名字都引入到全局命名空间中(因为全局命名空间是同时包含std
和任何用户声明命名空间的最近命名空间),这可能导致不合预期的名字冲突。通常认为,在头文件的文件作用域中采用它或其他的 using 指令是不良的实践。
参考
- C++ Primer
- cppreference 之命名空间