定义
命名空间花括号中由声明和定义组成,只要能出现在全局中的声明就能放在命名空间中,主要有:类、变量(及其初始化操作)、函数(及其定义)、模板和其他命名空间。声明在头文件中的命名空间被多个文件包含时有重定义的风险。
//创建namespace
//namespace只能在全局或在namespace中声明
namespace A
{
int a = 2;
class Maker;
}
namespace B
{
int a = 3;
namespace C
{
int a = 3;
}
}
void test(void)
{
//错误的声明
/*namespace A
{
int a = 2;
}*/
cout << "A::a: " << A::a << endl;
cout << "B::a: " << B::a << endl;
}
作用域
定义在某个命名空间中的名字可以被该命名空间内的其他成员(包括这些成员的内嵌作用域中的任何单位)直接访问。
在命名空间中定义时,无需使用域名前缀,在命名空间外部定义时需要明确指出域名前缀。
在命名空间中定义时,只能在本命名空间中(不能在嵌套的命名空间或其他类等内嵌的作用域中)。
在命名空间外定义时,定义必须出现在所属命名空间的外层空间中(或者更外层),不能在并列的内嵌作用域中。如果命名空间是在全局中声明的则只能在全局中进行定义。
模板特例化声明必须在原始模板所属的命名空间中。定义遵循上方规律。
全局作用域中定义的名字被隐式的添加到全局命名空间中。可以使用作用域运算符::进行调用(e.g. ::a)。
内层命名空间声明的名字将隐藏外层命名空间声明的同名成员(不同名的可以直接访问),隐藏的需要用作用域运算符加域名访问(只用作用域运算符只能访问最外层的全局空间)。内层命名空间中的名字只在内层命名空间中有效,外层访问时需要加前缀。
inline namespace(内联命名空间) 可以通过外层命名空间的名字直接访问。inline必须出现在命名空间第一次定义的地方,后面可省略(也可以写)。
unnamed namespace(未命名的命名空间) 效果等同声明静态全局变量(可在头文件中被多个文件包含,会为每个文件创建一个内存单元,互不干扰,只在本文件的作用范围起作用,在整个生命周期中有效),在全局中定义可直接使用,需要注意不能和全局作用域中的名字重复。
namespace A
{
int a = 2;
void func1(void);
namespace B
{
int a = 3;
//查找规则向外,不能定义A中的func1
// void A::func1(void)
// {
// cout << "a test" << endl;
// }
void func1(void)
{
cout << "a test" << endl;
}
}
}
//在定义命名空间时,如果命名空间已经存在则打开已经存在的命名空间定义并为其添加一些新的成员声明,否则创建一个新的命名空间。
namespace A
{
void func(void)//在命名空间中定义时
{
cout << "func" << endl;
}
}
void A::func1(void)//在命名空间外定义时
{
cout << "func1" << endl;
}
void test(void)
{
cout << "A::a: " << A::a << endl;
cout << "B::a: " << A::B::a << endl;
A::func();
A::func1();
A::B::func1();
}
namespace A
{
inline namespace B
{
void func1(void)
{
cout << "func1" << endl;
}
}
}
namespace A
{
void func(void)
{
cout << "func" << endl;
}
}
void test(void)
{
A::func();
A::func1();//可以通过外层命名空间的名字直接访问
}
使用
namespace alias:namespace开始,后面跟别名、=、原来的名字及分号。
namespace myA = A;//别名声明方法
using declaration:using可以出现在全局作用域、局部作用域、命名空间作用域、类的作用域。using一次只能引入一个成员,作用域和普通变量的声明一样。在类中使用using声明来改变派生类中继承的名字的访问限制,只对能被访问的名字有效,且可以对using声明设置访问权限。
namespace A
{
int a = 2;
namespace B
{
int a = 3;
}
}
void test(void)
{
//使用using声明,有重定义的风险,a重定义
//using A::a;
//using A::B::a;
//int a = 10;
//正确的声明,using的声明的作用域和普通变量的声明一样
using A::a;
cout << "A::a: " << a << endl;
cout << "A::B::a: " << A::B::a << endl;
}
class F
{
public:
void func(void)
{
cout << "func" << endl;
}
protected:
int a;
};
//使用using声明来改变派生类中继承的名字的访问限制
class S : private F
{
public:
using F::func;
void setVal(void)
{
a = 10;
cout << a << endl;
}
public:
using F::a;
};
void test(void)
{
S s;
s.func();
s.setVal();
cout<< s.a << endl;
}
using directive:使所有名字可见。可以出现在全局作用域、局部作用域、命名空间作用域,不能在类中出现。
//1、using namespace A;和普通变量声明一样有作用域,可以重复声明
//2、using namespace A;必须声明在命名空间的定义后面
//3、using namespace A;有重定义的风险,尽量不要在头文件中使用
namespace A
{
int a = 2;
namespace B
{
int a = 3;
}
}
void test(void)
{
using namespace A;
cout << a << endl;
cout << B::a << endl;
}
命名空间内部名字查找规则
由内向外依次查找每个外层作用域。命名空间中的类会先在类中查找(包括基类),接着在外层作用域中查找。
例外:当我们给函数传递一个类类型的对象时,除了常规的作用域查找外还会查找实参类所属的命名空间(e.g. "<<"运算符的重载不需要显式的使用作用域运算符进行调用)。
using与重载
using declaration声明的只是一个名字,所有重载函数的所有版本都被引入到当前作用域中。可嵌套重复声明,会屏蔽外层的同名函数,并与本作用域的同名并同参数的函数冲突,不同参数列表则扩充重载函数列表(不能扩充外层作用域的同名函数,因为已经被屏蔽)。
namespace A
{
void func(void);
void func(int);
void func(double);
}
void A::func(void)
{
cout << "func void" << endl;
}
void A::func(int)
{
cout << "func int" << endl;
}
void A::func(double)
{
cout << "func long" << endl;
}
void func(void)
{
cout << "Global func" << endl;
}
//using A::func;//引起重定义
void test(void)
{
using A::func;//using声明了这个重载函数的所有集合。
func();
func(1);
func(1.0);
::func();//外层作用域的被屏蔽,用作用域运算符调用
}
using directive可嵌套重复声明,与using声明不同,引入同名同参数列表的函数不会产生错误,但是需要通过作用域运算符::明确调用函数的版本。多个using directive可以同时引入同名同参数列表的重载函数。
namespace A
{
void func(void);
void func(int);
void func(double);
}
void A::func(void)
{
cout << "func void" << endl;
}
void A::func(int)
{
cout << "func int" << endl;
}
void A::func(double)
{
cout << "func long" << endl;
}
void func(void)
{
cout << "Global func" << endl;
}
void func(int)
{
cout << "Global func int" << endl;
}
void test(void)
{
//重定义函数可以用::来区分
using namespace A;
A::func();
A::func(1);
func(1.0);
::func();
::func(1);
}