C++命名空间详解
使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突。在C++中,变量、函数和类都是大量存在的。如果没有命名空间,这些变量、函数、类的名称将都存在于全局命名空间中,会导致很多冲突。比如,如果我们在自己的程序中定义了一个函数toupper(),这将重写标准库中的toupper()函 数,这是因为这两个函数都是位于全局命名空间中的。命名冲突还会发生在一个程序中使用两个或者更多的第三方库的情况中。此时,很有可能,其中一个库中的名 称和另外一个库中的名称是相同的,这样就冲突了。这种情况会经常发生在类的名称上。比如,我们在自己的程序中定义了一个Stack类,而我们程序中使用的某个库中也可能定义了一个同名的类,此时名称就冲突了。
Namespace 关键字的出现就是针对这种问题的。由于这种机制对于声明于其中的名称都进行了本地化,就使得相同的名称可以在不同的上下文中使用,而不会引起名称的冲突。或许命名空间最大的受益者就是C++中的标准库了。在命名空间出现之前,整个C++库都是定义在全局命名空间中的(这当然也是唯一的命名空间)。引入命名空间后,C++库就被定义到自己的名称空间中了,称之为std。这样就减少了名称冲突的可能性。我们也可以在自己的程序中创建自己的命名空间,这样可以对我们认为可能导致冲突的名称进行本地化。这点在我们创建类或者是函数库的时候是特别重要的。
命名空间基础
namespace 关键字使得我们可以通过创建作用范围来对全局命名空间进行分隔。本质上来讲,一个命名空间就定义了一个范围。定义命名空间的基本形式如下:
namespace 名称{//声明}
在命名空间中定义的任何东西都局限于该命名空间内。
下面就是一个命名空间的例子,其中对一个实现简单递减计数器的类进行了本地化。在该命名空间中定义了计数器类用来实现计数;其中的upperbound和lowerbound用来表示计数器的上界和下界。
///演示命名空间
1 namespace CounterNameSpace
{ int upperbound;
int lowerbound; class counter
{ int count;
public:
counter(int n)
{ if ( n <= upperbound ){
count = n;
} else {
count = upperbound;
}
}
void reset(int n)
{
if ( n < upperbound )
{
count = n;
}
}
int run() {
if ( count > lowerbound)
{
return count--;
} else {
return lowerbound;
}
}
};
}
其中的upperbound,lowerbound和类counter都是有命名空间CounterNameSpace定义范围的组成部分。
在命名空间中声明的标识符是可以被直接引用的,不需要任何的命名空间的修饰符。例如,在CounterNameSapce命名空间中,run()函数中就可以直接在语句中引用lowerbound:
///在命名空间中使用变量
if ( count > lowerbound)
{
return count--;
}
然而,既然命名空间定义了一个范围,那么我们在命名空间之外就需要使用范围解析运算符来引用命名空间中的对象。例如,在命名空间CounterNameSpace定义的范围之外给upperbound赋值为10,就必须这样写:
CounterNameSpace::upperbound = 10;
或者在CounterNameSpace定义的范围之外想要声明一个counter类的对象就必须这样写:
CounterNameSpace::counter obj;
一般来讲,在命名空间之外想要访问命名空间内部的成员需要在成员前面加上命名空间和范围解析运算符。
下面的程序演示了如何使用CounterNameSpace这个命名空间:
#include <iostream>
using namespace std;
namespace CounterNameSpace
{
int upperbound;
int lowerbound; class counter
{
int count;
public:
counter(int n)
{
if ( n <= upperbound )
{
count = n;
}
else
{
count = upperbound;
}
}
void reset(int n)
{
if ( n < upperbound )
{
count = n;
}
}
int run()
{
if ( count > lowerbound)
{
return count--;
}
else
return lowerbound;
}
};
} int main()
{
CounterNameSpace::upperbound = ;
CounterNameSpace::lowerbound = ;
CounterNameSpace::counter ob1();
int i;
do
{
i = ob1.run();
cout << i << " ";
}
while (i > CounterNameSpace::lowerbound);
cout << endl;
CounterNameSpace::counter ob2();
do
{
i = ob2.run();
cout << i << " ";
}
while (i > CounterNameSpace::lowerbound); cout << endl;
ob2.reset();
do
{
i = ob2.run();
cout << i << " "; }
while (i > CounterNameSpace::lowerbound);
cout << endl;
return ;
}
请注意:counter类以及upperbound和lowerbound的引用都是在前面加上了CounterNameSpace修饰符。但是,一旦声明了counter类型的对象,就没有必须在对该对象的任何成员使用这种修饰符了。因此ob1.run()是可以被直接调用的。其中的命名空间是可以被解析的。
相同的空间名称是可以被多次声明的,这种声明向相互补充的。这就使得命名空间可以被分割到几个文件中甚至是同一个文件的不同地方中。例如:
namespace NS { int i; } //... namespace NS { int j; }
其中命名空间NS被分割成两部分,但是两部分的内容却是位于同一命名空间中的。也就是NS。最后一点:命名空间是可以嵌套的。也就是说可以在一个命名空间内部声明另外的命名空间。
using关键字
如果在程序中需要多次引用某个命名空间的成员,那么按照之前的说法,我们每次都要使用范围解析符来指定该命名空间,这是一件很麻烦的事情。为了解决这个问题,人们引入了using关键字。using语句通常有两种使用方式:
using namespace 命名空间名称;
using 命名空间名称::成员;
第一种形式中的命名空间名称就是我们要访问的命名空间。该命名空间中的所有成员都会被引入到当前范围中。也就是说,他们都变成当前命名空间的一部分了,使用的时候不再需要使用范围限定符了。第二种形式只是让指定的命名空间中的指定成员在当前范围中变为可见。我们用前面的CounterNameSpace来举例,下面的using语句和赋值语句都是有效的:
using CounterNameSpace::lowerbound; //只有lowerbound当前是可见的
lowerbound = 10; //这样写是合法的,因为lowerbound成员当前是可见的
using CounterNameSpace; //所有CounterNameSpace空间的成员当前都是可见的
upperbound = 100; //这样写是合法的,因为所有的CounterNameSpace成员目前都是可见的
下面是我们对之前的程序进行修改的结果:
///使用using #include <iostream> using namespace std; namespace CounterNameSpace { int upperbound; int lowerbound; class counter { int count; public: counter(int n) { if ( n < upperbound) { count = n; } else { count = upperbound; } } void reset( int n ) { if ( n <= upperbound ) { count = n; } } int run() { if ( count > lowerbound ) { return count--; }
else
{
return lowerbound;
}
}
};
}
int main() { //这里只是用CounterNameSpace中的upperbound using CounterNameSpace::upperbound; //此时对upperbound的访问就不需要使用范围限定符了 upperbound = ; //但是使用lowerbound的时候,还是需要使用范围限定符的 CounterNameSpace::lowerbound = ; CounterNameSpace::counter ob1(); int i; do { i = ob1.run(); cout << i << " "; }while( i > CounterNameSpace::lowerbound); cout << endl; //下面我们将使用整个CounterNameSpace的命名空间 using namespace CounterNameSpace; counter ob2(); do { i = ob2.run(); cout << i << " "; }while( i > CounterNameSpace::lowerbound); cout << endl; ob2.reset(); lowerbound = ; do { i = ob2.run(); cout << i << " "; }while( i > lowerbound); return ; } 上面的程序还为我们演示了重要的一点:当我们用using引入一个命名空间的时候,如果之前有引用过别的命名空间(或者同一个命名空间),则不会覆盖掉对之前的引入,而是对之前引入内容的补充。也就是说,到最后,上述程序中的std和CounterNameSpace这两个命名空间都变成全局空间了。 没有名称的命名空间 有一种特殊的命名空间,叫做未命名的命名空间。这种没有名称的命名空间使得我们可以创建在一个文件范围里可用的命名空间。其一般形式如下: namespace { //声明 }
我们可以使用这种没有名称的命名空间创建只有在声明他的文件中才可见的标识符。也即是说,只有在声明这个命名空间的文件中,它的成员才是可见的,它的成员才是可以被直接使用的,不需要命名空间名称来修饰。对于其他文件,该命名空间是不可见的。我们在前面曾经提到过,把全局名称的作用域限制在声明他的文件的一种方式就是把它声明为静态的。尽管C++是支持静态全局声明的,但是更好的方式就是使用这里的未命名的命名空间。
std命名空间
标准C++把自己的整个库定义在std命名空间中。这就是本书的大部分程序都有下面代码的原因:
using namespace std;
这样写是为了把std命名空间的成员都引入到当前的命名空间中,以便我们可以直接使用其中的函数和类,而不用每次都写上std::。当然,我们是可以显示地在每次使用其中成员的时候都指定std::,只要我们喜欢。例如,我们可以显示地采用如下语句指定cout:
std::cout << “显示使用std::来指定cout”;
如果我们的程序中只是少量地使用了std命名空间中的成员,或者是引入std命名空间可能导致命名空间的冲突的话,我们就没有必要使用using namespace std;了。然而,如果在程序中我们要多次使用std命名空间的成员,则采用using namespace std;的方式把std命名空间的成员都引入到当前命名空间中会显得方便很多,而不用每次都单独在使用的时候显示指定
转载地址:http://blog.renren.com/share/730973714/7874424429
C++命名空间详解的更多相关文章
- linux命名空间详解_转
转自: Linux的命名空间详解--Linux进程的管理与调度(二) Linux Namespaces机制提供一种资源隔离方案. PID,IPC,Network等系统资源不再是全局性的,而是属于特定的 ...
- jquery的事件命名空间详解
jquery现在的事件API:on,off,trigger支持带命名空间的事件,当事件有了命名空间,就可以有效地管理同一事件的不同监听器,在定义组件的时候,能够避免同一元素应用到不同组件时,同一事件类 ...
- XML命名空间详解
http://happylongnv.blog.hexun.com/48859954_d.html 目的:解决同一个元素在相同文件中代表不同含义的问题.因为XML文档中使用的元素不是固定的,那么两个不 ...
- django 命名空间详解
include(module[, namespace=None, app_name=None ]) include(pattern_list) include((pattern_list, app_n ...
- SilverLight命名空间详解-新手入门
1.核心命名空间 1.xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"是silverlight的核 ...
- C#命名空间详解namespace
命名空间是一个域,这在个域中所有的类型名字必须是唯一的,不同的类型分组归入到层次化的命名空间, 命名空间的好处是:1.避免名字冲突,2.便于查找类型名字. 如:System.secruity.Cry ...
- c++ namespace命名空间详解
What is a namespace? A namespace defines an area of code in which all identifiers are guaranteed to ...
- Linux的命名空间详解--Linux进程的管理与调度(二)【转】
Linux Namespaces机制提供一种资源隔离方案. PID,IPC,Network等系统资源不再是全局性的,而是属于特定的Namespace.每个Namespace里面的资源对其他Namesp ...
- Linux的命名空间详解--Linux进程的管理与调度(二)
转自:http://blog.csdn.net/gatieme/article/details/51383322 日期 内核版本 架构 作者 GitHub CSDN 2016-05-12 Linux- ...
- c++的重载 缺省参数和命名空间详解
参加了几次笔试,发现有很多c++方面的问题被卡了.从现在开始进攻c++.之后会陆续更新c++学习笔记. 先说说我学习的书籍,大家如果有好的书籍推荐,感谢留言. 暂时是在看这些书自学. 1.C++介绍. ...
随机推荐
- xcode4中build Settings常见参数解析
本文转载至 http://shiminghua234.blog.163.com/blog/static/263912422012411103526386/ 1.Installation Dir ...
- bash shell最基本的语法
1 shell语句的基本构成 shell每个基本的构成元素之间都相隔一个空格. 比如[ -e file ],[.-e.file.]这四个基本元素之间都相隔了一个空格. 同样的道理[ ! -e file ...
- REST – PUT vs POST
REST – PUT vs POST – REST API Tutorial https://restfulapi.net/rest-put-vs-post/ REST – PUT vs POST I ...
- YARN commands are invoked by the bin/yarn script.
Apache Hadoop 2.9.0 – YARN Commands http://hadoop.apache.org/docs/stable/hadoop-yarn/hadoop-yarn-sit ...
- 浅谈 React、Flux 与 Redux
React React 是一个 View 层的框架,用来渲染视图,它主要做几件事情: 组件化利用 props 形成单向的数据流根据 state 的变化来更新 view利用虚拟 DOM 来提升渲染性能 ...
- 20170225-ALV tree 显示
1.写程序, 2.话屏幕9000,CALL SCREEN 9000.(双击屏幕进去画个容器就ok,+OK_CODE,+去掉注释) 3.处理好ALV 的PBO 初始化,处理用户事件PAO,user co ...
- ossfs常见配置错误
以下问题出现在非root用户下 执行echo ××××> /etc/passwd-ossfs bash: /etc/passwd-ossfs: Permission denied 使用sudo ...
- Spark理论学习笔记(一)
1.调度 分为FIFO和FAIR两种模式 创建调度池:sc.setLocalProperty("spark.scheduler.pool", "pool6") ...
- Linux内核中工作队列的使用work_struct,delayed_work【转】
本文转载自:http://blog.csdn.net/zahuopuboss/article/details/43268983 初始化工作队列 调度工作队列 取消工作队列 #include <l ...
- Java 字符串比较小知识
使用 String.compareTo 方法: compareTo() 的返回值是int, 它是先比较对应字符的大小(ASCII码顺序). 1.如果字符串相等,返回值0. 2.如果第一个字符和参数的第 ...