为什么要用C++之:命名空间
2009-11-29 12:24
155 查看
1.1.1. 为什么要命名空间(namespace)
对于一个大型的C语言软件项目,给函数和全局变量起名不是一个容易的事情,因为必须考虑有没有可能与其它程序员写的代码冲突,多数的做法是对每个模块的一组函数名加个特定前缀,如HTRequest_setInternal、HTRequest_internal等。这使得程序员每次调用这些函数时都必须多输出一些字符,虽然使用现在比较优秀的IDE(Integrated Development Environment),不会给程序员的输入带来多少负责,但这些字符看起来还是有些多余。所以C++引入了namespace的概念,把一些标识符以命名空间树结构的方式组织起来,使代码看起来更优雅。而且事实证明,该特性是先进的,对于大型项目的作用是明显的,并且在后来的编程语言如Java、C#、Python都支持此类特性,只是有些叫法不同而已。
命名空间不仅可以用于组织类型(class、struct、Enum)等,还可以用于组织全局变量、全局函数等。如例程[2-1]所示,将不同模块的标识符分别组织到不同的命名空间中,从而避免标识符的冲突。
在大型的C++项目中使用命名空间比较好的项目如Google浏览器Chorme、开源C++库boost等,而没有使用命名空间的一个例子就是开源C++库ACE(The ADAPTIVE Communication Environment ),它选择了在每个类型的前面加上前缀“ACE_”,使得标识符都比较长,而且看起来有点儿冗余。为使用起来方便,而且不修改ACE的源码,可以使用typedef标识符对这些标识符进行重命名,如例程[2-2]所示。请注意,不能在这里使用#define,因为宏不受命名空间的限制。
1.1.2. 如何引用命令空间内的标识符
当引用的标识符不在当前命名空间或全局命名空间内时,有三种方式可以引用该标识符,如引用前一节新定义的ace命令空间中的Mutex类型:
方式一只在必要的时候通过域运算符“::”引用指定命令空间内的标识符,适用于当前编译单元引用ace内的标识符不多,而且编译单元内使用这些标识符的次数也不多的情况。
方式二只引入ace::Mutex一个标识符,如果在当前编译单元内使用ace::Mutex次数较多,而且不会与当前命名空间内的标识符冲突,建议使用这种方式。
方式三是把ace命名空间中的全部标识符都引入到当前命名空间中,此后ace所有的标识符对于当前命名空间都是可见的,这会提高标识符冲突的危险。如果当前编译单元用到ace命令空间内的标识符较多,而且不会出现标识符冲突的问题,可以使用这种方式,以减少字符的输入。
对于以上三种方式,建议优先选择第一种,这种方式最不容易产生标识符冲突,方式二次之,尽可能不用第三种试,即使是对于C++标准库也不要使用第三种方式,因为至少在Solaris系统中就有一个struct类型叫map ??,如果你引用了包含该类型的头文件就会导致命名冲突。
另外,建议不要在头文件中使用using语句引入标识符,否则这些标识符将被暴露到引用这个头文件的所有编译单元内,这样很容易使命名空间失去其作用而产生命名冲突。
对于用到的系统API,建议函数名前使用域运算符加以区别,使程序可读性更好,如:::GetLastError( ), ::getcwd( )。
注意,切忌在自定义的命名空间中引用系统头文件,如例程[2-3]所示,避免造成标识符的混乱。
1.1.3. 命令空间的别名
当要引用的命名空间比较长,而且想用第一种方式引用命名空间内的实体,则可以通过命名空间别名,为原来的命名空间起个简短的名字,如例程[2-4]。
1.1.4. 匿名命令空间
当声明命名空间时的名称为空时,则该命名空间为匿名命名空间(unnamed namespace)。匿名的空间是C++用于替代使用static定义作用域为本编译单元的全局函数或全局变量的一种新的替代方式,匿名空间与命名的命名空间一样可以嵌套。由于匿名命名空间没有命名空间的名字,所以也无法在其它的编译单元内通过extern声明该变量,于是该变量自然也只在本编译单元内可见,如例程[2-5]。
使用匿名空间比使用static至少有两个好处:
1) 对于一组多个标识符函数只需要使用一个匿名空间来声明,不需要多次输入static。
2) 可以嵌套。这样可以在不同命名空间中使用多个同名的标识符。
在C++的标准中也建议使用匿名命名空间间定义编译单元内部的全局变量,替代static,static关键词在此处被认为是过期的(deprecated)特性。
对于一个大型的C语言软件项目,给函数和全局变量起名不是一个容易的事情,因为必须考虑有没有可能与其它程序员写的代码冲突,多数的做法是对每个模块的一组函数名加个特定前缀,如HTRequest_setInternal、HTRequest_internal等。这使得程序员每次调用这些函数时都必须多输出一些字符,虽然使用现在比较优秀的IDE(Integrated Development Environment),不会给程序员的输入带来多少负责,但这些字符看起来还是有些多余。所以C++引入了namespace的概念,把一些标识符以命名空间树结构的方式组织起来,使代码看起来更优雅。而且事实证明,该特性是先进的,对于大型项目的作用是明显的,并且在后来的编程语言如Java、C#、Python都支持此类特性,只是有些叫法不同而已。
命名空间不仅可以用于组织类型(class、struct、Enum)等,还可以用于组织全局变量、全局函数等。如例程[2-1]所示,将不同模块的标识符分别组织到不同的命名空间中,从而避免标识符的冲突。
// 例程[2-1] #include namespace sock{ typedef unsigned short socket_port_t; const char* LOOPBACK_ADDR = “127.0.0.1”; const socket_port_t DEFUALT_HTTP_PORT = 80; } int main( void ) { std::cout<<”Local HTTP addr = “< <<’:’< return 0; }
在大型的C++项目中使用命名空间比较好的项目如Google浏览器Chorme、开源C++库boost等,而没有使用命名空间的一个例子就是开源C++库ACE(The ADAPTIVE Communication Environment ),它选择了在每个类型的前面加上前缀“ACE_”,使得标识符都比较长,而且看起来有点儿冗余。为使用起来方便,而且不修改ACE的源码,可以使用typedef标识符对这些标识符进行重命名,如例程[2-2]所示。请注意,不能在这里使用#define,因为宏不受命名空间的限制。
// 例程[2-2] #include namespace ace{ typedef ACE_Mutex Mutex; typedef ACE_Lock Lock; }
1.1.2. 如何引用命令空间内的标识符
当引用的标识符不在当前命名空间或全局命名空间内时,有三种方式可以引用该标识符,如引用前一节新定义的ace命令空间中的Mutex类型:
// 方式一 ace::Mutex mutex; // 方式二 using ace::Mutex; Mutex mutex; // 方式三 using namespace ace; Mutex mutex;
方式一只在必要的时候通过域运算符“::”引用指定命令空间内的标识符,适用于当前编译单元引用ace内的标识符不多,而且编译单元内使用这些标识符的次数也不多的情况。
方式二只引入ace::Mutex一个标识符,如果在当前编译单元内使用ace::Mutex次数较多,而且不会与当前命名空间内的标识符冲突,建议使用这种方式。
方式三是把ace命名空间中的全部标识符都引入到当前命名空间中,此后ace所有的标识符对于当前命名空间都是可见的,这会提高标识符冲突的危险。如果当前编译单元用到ace命令空间内的标识符较多,而且不会出现标识符冲突的问题,可以使用这种方式,以减少字符的输入。
对于以上三种方式,建议优先选择第一种,这种方式最不容易产生标识符冲突,方式二次之,尽可能不用第三种试,即使是对于C++标准库也不要使用第三种方式,因为至少在Solaris系统中就有一个struct类型叫map ??,如果你引用了包含该类型的头文件就会导致命名冲突。
另外,建议不要在头文件中使用using语句引入标识符,否则这些标识符将被暴露到引用这个头文件的所有编译单元内,这样很容易使命名空间失去其作用而产生命名冲突。
对于用到的系统API,建议函数名前使用域运算符加以区别,使程序可读性更好,如:::GetLastError( ), ::getcwd( )。
注意,切忌在自定义的命名空间中引用系统头文件,如例程[2-3]所示,避免造成标识符的混乱。
// 例程[2-3] namespace my_space{ #include }
1.1.3. 命令空间的别名
当要引用的命名空间比较长,而且想用第一种方式引用命名空间内的实体,则可以通过命名空间别名,为原来的命名空间起个简短的名字,如例程[2-4]。
// 例程[2-4] namespace long_namespace{ void func( void ) { /* function body */ } } namespace ns = long_namespace; int main( void ) { ns::func(); return 0; }
1.1.4. 匿名命令空间
当声明命名空间时的名称为空时,则该命名空间为匿名命名空间(unnamed namespace)。匿名的空间是C++用于替代使用static定义作用域为本编译单元的全局函数或全局变量的一种新的替代方式,匿名空间与命名的命名空间一样可以嵌套。由于匿名命名空间没有命名空间的名字,所以也无法在其它的编译单元内通过extern声明该变量,于是该变量自然也只在本编译单元内可见,如例程[2-5]。
// 例程[2-5] #include using namespace std; namespace{ int i = 256; } namespace ns{ namespace { int i = 128; } void func(void) { cout<<"ns::func :" < cout<<"/t::i="<<::i< cout<<"/tns::i="< } } int main(void ) { cout<<::i< cout<<"i="< cout<<"ns::i="< ns::func(); return 0; }
使用匿名空间比使用static至少有两个好处:
1) 对于一组多个标识符函数只需要使用一个匿名空间来声明,不需要多次输入static。
2) 可以嵌套。这样可以在不同命名空间中使用多个同名的标识符。
在C++的标准中也建议使用匿名命名空间间定义编译单元内部的全局变量,替代static,static关键词在此处被认为是过期的(deprecated)特性。
相关文章推荐
- c++静态成员函数为什么不能为虚函数?
- C++析构函数为什么要为虚函数
- 【c++】模板为什么不支持分离编译
- C++析构函数为什么要为虚函数
- 为什么Java永远比C++慢?
- 转[互联网面试笔试汇总C/C++-27] 为什么TCP链接要三次握手而不是两次握手-雅虎
- C++中基类的析构函数为什么要用virtual虚析构函数
- 在c/c++语言中,为什么c[5] == 5[c]
- 你的C/C++程序为什么无法运行?揭秘Segmentation fault
- 为什么C++支持重载,C语言不支持
- [zz]为什么应该用模块取代C/C++中的头文件?
- 为什么应该用模块取代C/C++中的头文件?
- C++为什么不叫++C???
- c++赋值运算符为什么要返回引用?
- C++通过基类指针delete派生类数组,析构函数是虚函数,程序为什么会崩溃? https://www.zhihu.com/question/30838092/answer/49623765
- 为什么一般不用一个整数给一个指针变量赋初值?[c/c++]
- C/C++ 为什么在函数内部修改形参并不能真正地改变传入形参的值?
- C# PDF Page操作——设置页面切换按钮 C# 添加、读取Word脚注尾注 C#为什么不能像C/C++一样的支持函数只读传参 web 给大家分享一个好玩的东西,也许你那块就用的到
- 为什么在C++使用pthread_create()的时候,类成员函数做线程的处理函数必须要定义成static类型的?
- c++谭浩强教材教学练习例题1.2 求两数之和 为什么sum=a+b;sum的值为65538