C++命名空间namespace及using的使用

    命名空间(namespace)是C++语言特别重要的特性,当第三方供应商提供的库时,为了避免与其他供应商或者用户定义的名字相冲突(命名空间污染),常常将库的内容放置在自己独立的命名空间中。C++标准库也定义了相应命名空间std,用户在使用标准库时必须通过作用域运算符(::),或者使用using关键词来简化命名空间中名字的使用。

  • 命名空间的定义

    C++通过作用域确定变量的访问权限,如全局作用域(对应全局变量)、函数作用域(对应函数局部对象)、类作用域(对应类成员)等等。命名空间对应着相应的作用域,可以将全局作用域进行分割,这样在不同命名空间定义的名字即使相同也不会发生冲突。C++中定义命名空间的语法为:

namespace myName{
//相关声明及定义
}

    命名空间可以是不连续的,可以定义在几个不同的部分甚至不同的文件,这种组织方式允许我们将接口和实现分开;我们仍然可以在头文件中声明函数,在源文件中再实现它,只要他们处于同一个命名空间即可。我们也可以不在命名空间内部定义函数,如:

namespace myName{
    void fun();
}
void myName::fun(){
//相关操作
}

    作用域运算符(::)表明fun是属于naName这个作用域的。namespace可以嵌套使用,如:

namespace myName{
    namespace MyName2{
    //相关声明及定义
    } 
}
    如果命名空间不指定名字,则里面所有的声明都相当于静态声明,即只能在当前文件中可以访问且可直接访问,相当于C语言的static声明。C++11已经将文件中进行静态声明的方法取消了,转到使用 未命名的命名空间
  • 命名空间的访问

    对于用户来说,如何访问供应商提供的含命名空间的库是极其重要的。以C++标准库为例,C++标准库定义在名字为std的命名空间中。IO库(输入输出库)iostream是经常使用的标准库之一,cin和cout分别为标准输入和标准输出。

#include<iostream>

int main(int argc,char **argv){
    cout<<"hello world!"<<endl;
}

    上述的代码试图打印经典程序"hello world!",但是在编译时却报出"error: 'cout' was not declared in this scope"和"error: 'endl' was not declared in this scope"这两个错误。原因在于coutendl定义于std命名空间中,不能直接访问。以下是通过作用域运算符进行访问的例子:

#include<iostream>

int main(int argc,char **argv){
    std::cout<<"hello world!"<<std::endl;
}

    通过作用域运算符(::)显式指出coutstd这两个名字属于std命名空间,可以对其正确访问。如果程序代码很长,经常要使用cout的话,每次都增加前缀std十分繁琐,为了减轻程序员的负担,C++提供了using关键字。可以在使用标准库对象前对对象进行using声明

#include<iostream>
using std::cout;
using std::endl;
int main(int argc,char **argv){
    cout<<"hello world!"<<endl;
}

    通过using声明,程序中便可以直接使用coutendl了。using声明引入的名字的作用域满足一般的作用域规则:有效范围从using声明的地方开始,到其语句所在的作用域结束时为止。也就是说,using声明可以出现在局部作用域(如函数作用域等等)。有个特殊的地方是类作用域中只能声明基类成员。

    如果需要使用的标准库对象比较多,一条一条的使用using声明的确有点费事,C++语法提供了另一种简化的方式using指示

#include<iostream>
using namespace std;
int main(int argc,char **argv){
    cout<<"hello world!"<<endl;
}

    using指示强制将std中的名字全部注入,这样无论引入何种标准库对象,均不用再增加前缀std:: 。这种做法看似十分便捷,但由于把所有的对象名字均暴露出来,也就相当于违背了防止命名空间污染的原则。很多C++程序常常使用这种方法来引入标准库,这其实是种非常偷懒的做法,不是很建议使用。不建议使用的原因还有一个,就是using指示的作用域同一般的作用域规则不同,会将作用域注入到命名空间的上一层中去。下面的代码来源于《c++ premier》:

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

    using指示将命名空间bilp中定义的名字仿佛提升到blip的上层空间,而不是manip的局部作用域。这带来问题便是,如果在上层空间中也定义了相同名字的变量,那么对该变量的直接使用会出现错误。这种错误在不使用该变量时完全不报错,也就是说提升的变量与原来的变量名字即使相同,只要不对其直接访问编译便可以正常通过。这带来的结果是,对于某些库有时用到其新特性时才会出现问题,无疑增加了维护的难度。所以在可以的情况下,尽量避免using指示。

    以上是对命名空间基础的介绍,编者能力有限如有说明不当还望指出。命名空间和using的特性还常常会影响函数重载、候选函数的查找这两个方面,这涉及C++标准更深入的方面,以及using的其他用法(如类型别名等等),在此就不进行赘述了。

  • 18
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值