C++命名空间

       有时候我们开发的程序需要来自多个独立开发的库,这些库又会定义大量的全局名字,如类、函数和模板等。当应用程序用到多个供应商提供的库时,不可避免地会发生某些名字冲突的情况,这会引发命名空间污染(将多个库名字放置在全局命名空间中)。
       命名空间为防止名字冲突提供了更加可控的机制。命名空间分割了全局命名空间,其中每个命名空间是一个作用域。通过在某个命名空间中定义库的名字,就可以避免全局名字固有的限制。


命名空间的定义
      一个命名空间的定义包含两部分:首先是关键字namespace,随后是命名空间的名字。在命名空间名字后面是一系列由花括号括起来的声明和定义,主要包括:类、变量、函数、模板和其他命名空间:

namespace cover{
      class base{/*...*/};
      base operator+{const base&,const base&};
      class quere{/*....*/};
      class quere_close{/*....*/};
}//命名空间结束后无须分号,这一点与块类相似

       这是一个名为cover的命名空间,包含三个类和一个重载的+运算符。
       和其他名字一样,命名空间的名字也必须在定义它的作用域内保持唯一。命名空间既可以定义在全局作用域内,也可以定义在其他命名空间中,但不能定义在函数或类内部。


每个命名空间都是一个作用域
       和其他作用域类似,命名空间中的每个名字都必须表示该空间内的唯一实体。因为不同命名空间的作用域不同,所以在不同命名空间内可以有相同名字的成员。
       定义在某个命名空间中的名字可以被该命名空间内的其他成员直接访问,也可以被这些成员内嵌作用域中的任何单位访问。位于该命名空间之外的代码则必须明确指出所用的名字属于哪个命名空间:

 cover::quere q=cover::quere("hello");

       如果其他命名空间(比如说snoey)也提供了一个名为quere的类,并且我们希望使用这个类替代cover中定义的同名类,则可以按照如下方式修改代码:

soney::quere q=cover::quere("hello");

       理解命名空间可以是不连续的。命名空间可以定义在几个不同的部分,这一点与其他作用域不一样。编写如下的命名空间定义:

namespace ui{
//相关声明
}

       可能是定义了一个名为ui的新命名空间,也可能是为已经存在的命名空间添加一些新成员。如果之前没有名为ui的命名空间的定义,则上述代码创建一个新的命名空间;否则,上述代码打开已经存在的命名空间定义并为其添加一些新成员的声明。
       命名空间的定义可以不连续的特性使得我们可以将几个独立的接口和实现文件组成一个命名空间。此时,命名空间的组织方式类似于我们管理的自定义类及函数的方式:
1、命名空间的一部分成员的作用是定义类,以及声明作为类接口的函数及对象,则这些成员应该置于头文件中,这些头文件将被包含在使用了这些成员的文件中。
2、命名空间成员的定义部分则置于另外的源文件中。

       在程序中某些实体只能定义一次:如非内联函数、静态数据成员、变量等,命名空间中定义的名字也需要满足这一要求,我们可以通过上面的方式组织命名空间并达到目的。这种接口和实现分离的机制确保我们所需的函数和其他名字之定义一次,而只要是用到这些实体的地方都能看到对于实体名字的声明。

命名空间的别名

       命名空间的别名即可以为命名空间的名字设定一个短的多的同义词。例如,一个很长的命名空间的的名字形如

 namespace close_open{/*...*/};

可以设定一个短的多的同义词:

namespace open=close_open;

       命名空间的别名声明以关键字namespace开始,后面是别名所用的名字、=符号、命名空间原来的名字以及一个分号。不能在命名空间还没有定义前就声明别名,否则将产生错误。
命名空间的别名也可以指向一个嵌套的命名空间:

namespace ofp=close_open::Quereofp;

一个命名空间可以有好几个同义词或别名,所有别名都与命名空间原来的名字等价。

using声明和using指示
      一条using声明语句一次只引入命名空间的一个成员,可以清楚地知道程序中所用的到底是哪个名字。using声明引入的名字遵循的作用域规则:它的有效范围从using声明的地方开始,一直到using声明所在的作用域结束为止。在此过程中,外层作用域的同名实体将被隐藏。未限定的名字只能在using声明所在的作用域以及内层作用域中使用。在有效作用域结束后必须使用完成的经过限定的名字。一条using声明语句可以出现在全局作用域、局部作用域、,命名空间作用域以及类的作用域中。在类的作用域中,这样的声明语句只能指向基类成员。using指示和using声明类似的地方是,可以使用命名空间名字的简写形式;和using声明不同的地方是,无法控制哪些名字是可见的,因为所有名字都是可见的。
      using指示以关键字using开始,后面是关键字namespace以及命名空间的名字。如果这里所用的名字不是一个已经定义好的命名空间的名字,则程序将发生错误。using指示可以出现在全局作用域、局部作用域和命名空间作用域中,但是不能出现在类的作用域中。
      using指示使得某个特定的命名空间中所有的名字都可见,这样无须再为它们添加任何前缀限定符了。简写的名字从using指示开始,一直到using指示所在的作用域结束都能使用。
注意:如果我们提供了一个对std等命名空间的using指示而未做任何特殊控制的话,将重新引入由于使用多个库而造成的的名字冲突问题。
 

using指示与作用域
       using声明的名字的作用域与using声明语句本身的作用域一致,从效果上看就好像using声明语句为命名空间的成员在当前作用域内创建了一个别名一样。using指示所做的绝非声明别名这么简单,它具有将命名空间成员提升到包含命名空间本身和using指示的最近作用域的能力。
       using声明和using指示在作用域上的区别直接决定了它们工作方式的不同。对于using声明来说,只是简单地令名字在局部作用域内有效。相反,using指示是令整个命名空间的所有内容变得有效。通常情况下,命名空间中会含有一些不能出现在局部作用域中的定义,因此,using指示一般被看做是出现在最近的外层作用域中。
       假定有一个命名空间A和一个函数f,它们都定义在全局作用域中。如果f含有一个对A的using指示,则在f看来,A中的名字仿佛是出现在全局作用域f之前的位置一样:
 

//命名空间A和f定义在全局作用域中
namespace A{
int i, j;
}
void f()
{
    using namespace A;    //把A中的名字注入到全局作用域中
    cout<<i * j<<endl;    //使用命名空间A中的i和j
    //...
}

using指示示例

namespace blip{
   int i=16,j=15,k=23;
   //其他声明
}
int j=0;
void manip()
{
   //using指示,blip中的名字被添加到全局作用域中
   using namespace blip; //如果使用了j,则将在::j和blip::j之间产生冲突
   ++i;                  //将blip::i设定为17
   ++j;                  //二义性错误:是全局的j还是blip::j?
   ++::j;                //正确:将全局的j设定为1
   ++blip::j;            //正确:将blip::j设定为16
   int k=97;             //当前局部的k隐藏了blip::k       
   ++k;                  //将当前局部的k设定为98
}

       manip的using指示使得程序可以直接访问blip的所有名字,也就是说,manip的代码可以使用blip中名字的简写形式。blip的成员看起来好像是定义在blip和manip所在的作用域一样。假定manip定义在全局作用域中,则blip的成员也好像定义在全局作用域中一样。当命名空间被注入到它的外层作用域之后,很有可能该命名空间中定义的名字会与其外层作用域中的成员冲突。例如在manip中,blip的成员j就与全局作用域中的j产生了冲突。这种冲突时允许存在的,但是要想使用冲突的名字,就必须明确指出名字的版本。manip中所有未加限定的j都会产生二义性错误。为了使用像 j 这样的名字,必须使用作用域运算符来明确之处所需的版本。使用::j来表示定义在全局作用域中的 j,而使用 blip::j 来表示定义在 blip 中的j 。
       因为manip的作用域和命名空间的作用域不同,所以manip内部的声明可以隐藏命名空间中的某些成员名字。例如,局部变量k隐藏了命名空间的成员blip::k.在manip内使用k不存在二义性,它指的就是局部变量k。
 

头文件与using声明或指示
       头文件如果在顶层作用域中含有using指示或using声明,则会将名字注入到所有包含了该头文件的文件中。通常情况下,头文件应该只负责定义接口部分的名字,而不定义实现部分的名字。因此,头文件最多在它的函数或命名空间内使用using指示或using声明。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值