C++命名空间的解释

2024-09-06 01:08
文章标签 c++ 解释 空间 命名

本文主要是介绍C++命名空间的解释,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突。在C++中,变量、函数和类都是大量存在的。如果没有命名空间,这些变量、函数、类的名称将都存在于全局命名空间中,会导致很多冲突。比如,如果我们在自己的程序中定义了一个函数toupper(),这将重写标准库中的toupper()函数,这是因为这两个函数都是位于全局命名空间中的。命名冲突还会发生在一个程序中使用两个或者更多的第三方库的情况中。此时,很有可能,其中一个库中的名称和另外一个库中的名称是相同的,这样就冲突了。这种情况会经常发生在类的名称上。比如,我们在自己的程序中定义了一个Stack类,而我们程序中使用的某个库中也可能定义了一个同名的类,此时名称就冲突了。

Namespace 关键字的出现就是针对这种问题的。由于这种机制对于声明于其中的名称都进行了本地化,就使得相同的名称可以在不同的上下文中使用,而不会引起名称的冲突。或许命名空间最大的受益者就是C++中的标准库了。在命名空间出现之前,整个C++库都是定义在全局命名空间中的(这当然也是唯一的命名空间)。引入命名空间后,C++库就被定义到自己的名称空间中了,称之为std。这样就减少了名称冲突的可能性。我们也可以在自己的程序中创建自己的命名空间,这样可以对我们认为可能导致冲突的名称进行本地化。这点在我们创建类或者是函数库的时候是特别重要的。

命名空间基础

namespace 关键字使得我们可以通过创建作用范围来对全局命名空间进行分隔。本质上来讲,一个命名空间就定义了一个范围。定义命名空间的基本形式如下:

namespace 名称{//声明}

在命名空间中定义的任何东西都局限于该命名空间内。

下面就是一个命名空间的例子,其中对一个实现简单递减计数器的类进行了本地化。在该命名空间中定义了计数器类用来实现计数;其中的upperboundlowerbound用来表示计数器的上界和下界。

演示命名空间

  1. namespace CounterNameSpace
  2. {

  3.     int upperbound;
  4.     int lowerbound;

  5.     class counter
  6.     {

  7.         int count;
  8.    public:
  9.      counter(int n)
  10.      {

  11.             if ( n <= upperbound ){
  12.                 count = n;
  13.             } else  {
  14.                 count = upperbound;
  15.             }
  16.       }
  17.     void reset(int n)
  18.      {
  19.        if ( n < upperbound )
  20.             {
  21.                 count = n;
  22.             }
  23.         }
  24.      int run() {
  25.       if ( count > lowerbound)
  26.        {
  27.                 return count--;
  28.        } else {
  29.                 return lowerbound;
  30.        }
  31.       }
  32.     };
  33. }

其中的upperboundlowerbound和类counter都是有命名空间CounterNameSpace定义范围的组成部分。

在命名空间中声明的标识符是可以被直接引用的,不需要任何的命名空间的修饰符。例如,在CounterNameSapce命名空间中,run()函数中就可以直接在语句中引用lowerbound

点击(此处)折叠或打开

  1. if ( count > lowerbound)

  2. {

  3. return count--;

  4. }

然而,既然命名空间定义了一个范围,那么我们在命名空间之外就需要使用范围解析运算符来引用命名空间中的对象。例如,在命名空间CounterNameSpace定义的范围之外给upperbound赋值为10,就必须这样写:

CounterNameSpace::upperbound = 10;

或者在CounterNameSpace定义的范围之外想要声明一个counter类的对象就必须这样写:

CounterNameSpace::counter obj;

一般来讲,在命名空间之外想要访问命名空间内部的成员需要在成员前面加上命名空间和范围解析运算符。

下面的程序演示了如何使用CounterNameSpace这个命名空间:

演示命名空间

  1. #include <iostream>
  2. using namespace std;
  3. namespace CounterNameSpace
  4. {
  5.     int upperbound;
  6.     int lowerbound;

  7.     class counter
  8.     {
  9.         int count;
  10.     public:
  11.         counter(int n)
  12.         {
  13.             if ( n <= upperbound )
  14.             {
  15.                 count = n;
  16.             } else
  17.             {
  18.                 count = upperbound;
  19.             }
  20.         }
  21.         void reset(int n)
  22.         {
  23.             if ( n < upperbound )
  24.             {
  25.                 count = n;
  26.             }
  27.         }
  28.         int run()
  29.         {
  30.           if ( count > lowerbound)
  31.             {
  32.                 return count--;
  33.             }
  34.             else
  35.                 return lowerbound;
  36.         }
  37.     };
  38. }

  39. int main()
  40. {
  41.  CounterNameSpace::upperbound = 100;
  42.  CounterNameSpace::lowerbound = 0;
  43.  CounterNameSpace::counter ob1(10);
  44.   int i;
  45.   do {
  46.    i = ob1.run();       
  47.    cout << i << " ";
  48.  } while (i > CounterNameSpace::lowerbound);
  49.  cout << endl;
  50.  CounterNameSpace::counter ob2(20);
  51. do {
  52.     i = ob2.run();
  53.     cout << i << " ";
  54.     } while (i > CounterNameSpace::lowerbound);

  55.     cout << endl;
  56.     ob2.reset(100);
  57.     do
  58.     {  i = ob2.run();
  59.         cout << i << " ";

  60.     } while (i > CounterNameSpace::lowerbound);
  61.     cout << endl;
  62.     return 0;
  63. }

请注意:counter类以及upperboundlowerbound的引用都是在前面加上了CounterNameSpace修饰符。但是,一旦声明了counter类型的对象,就没有必须在对该对象的任何成员使用这种修饰符了。因此ob1.run()是可以被直接调用的。其中的命名空间是可以被解析的。

相同的空间名称是可以被多次声明的,这种声明向相互补充的。这就使得命名空间可以被分割到几个文件中甚至是同一个文件的不同地方中。例如:

     

点击(此处)折叠或打开

  1. namespace NS

  2. {

  3. int i;

  4. }

  5.  

  6. //...


  7. namespace NS

  8. {

  9.     int j;

  10. }

其中命名空间NS被分割成两部分,但是两部分的内容却是位于同一命名空间中的。也就是NS。最后一点:命名空间是可以嵌套的。也就是说可以在一个命名空间内部声明另外的命名空间。

using关键字

如果在程序中需要多次引用某个命名空间的成员,那么按照之前的说法,我们每次都要使用范围解析符来指定该命名空间,这是一件很麻烦的事情。为了解决这个问题,人们引入了using关键字。using语句通常有两种使用方式:

using namespace 命名空间名称;

using 命名空间名称::成员;

第一种形式中的命名空间名称就是我们要访问的命名空间。该命名空间中的所有成员都会被引入到当前范围中。也就是说,他们都变成当前命名空间的一部分了,使用的时候不再需要使用范围限定符了。第二种形式只是让指定的命名空间中的指定成员在当前范围中变为可见。我们用前面的CounterNameSpace来举例,下面的using语句和赋值语句都是有效的:

using CounterNameSpace::lowerbound; //只有lowerbound当前是可见的

lowerbound = 10; //这样写是合法的,因为lowerbound成员当前是可见的

using CounterNameSpace; //所有CounterNameSpace空间的成员当前都是可见的

upperbound = 100; //这样写是合法的,因为所有的CounterNameSpace成员目前都是可见的

下面是我们对之前的程序进行修改的结果:

使用using


  1. #include <iostream>

  2.  

  3. using namespace std;

  4.  

  5. namespace CounterNameSpace

  6. {

  7.     int upperbound;

  8.     int lowerbound;

  9.     class counter

  10.     {

  11.         int count;

  12.     public:

  13.         counter(int n)

  14.         {

  15.             if ( n < upperbound)

  16.             {

  17.                 count = n;

  18.             }

  19.             else

  20.             {

  21.                 count = upperbound;

  22.             }

  23.         }

  24.  

  25.         void reset( int n )

  26.         {

  27.             if ( n <= upperbound )

  28.             {

  29.                 count = n;

  30.             }

  31.         }

  32.  

  33.         int run()

  34.         {

  35.             if ( count > lowerbound )

  36.             {

  37.                 return count--;

  38.             }
  1.             else
  2.             {
  3.                 return lowerbound;
  4.             }
  5.         }
  6.     };
  7. }
  8. int main()

  9. {

  10.     //这里只是用CounterNameSpace中的upperbound

  11.     using CounterNameSpace::upperbound;

  12.  

  13.     //此时对upperbound的访问就不需要使用范围限定符了

  14.     upperbound = 100;

  15.     //但是使用lowerbound的时候,还是需要使用范围限定符的

  16.     CounterNameSpace::lowerbound = 0;

  17.     CounterNameSpace::counter ob1(10);

  18.     int i;

  19.  

  20.     do

  21.     {

  22.         i = ob1.run();

  23.         cout << i << " ";

  24.     }while( i > CounterNameSpace::lowerbound);

  25.     cout << endl;

  26.  

  27.     //下面我们将使用整个CounterNameSpace的命名空间

  28.     using namespace CounterNameSpace;

  29.     counter ob2(20);

  30.     do

  31.     {

  32.         i = ob2.run();

  33.         cout << i << " ";

  34.     }while( i > CounterNameSpace::lowerbound);

  35.     cout << endl;

  36.  

  37.     ob2.reset(100);

  38.     lowerbound = 90;

  39.     do

  40.     {

  41.         i = ob2.run();

  42.         cout << i << " ";

  43.     }while( i > lowerbound);

  44.  

  45.     return 0;

  46. }

  47. 上面的程序还为我们演示了重要的一点:当我们用using引入一个命名空间的时候,如果之前有引用过别的命名空间(或者同一个命名空间),则不会覆盖掉对之前的引入,而是对之前引入内容的补充。也就是说,到最后,上述程序中的std和CounterNameSpace这两个命名空间都变成全局空间了。

  48. 没有名称的命名空间

  49. 有一种特殊的命名空间,叫做未命名的命名空间。这种没有名称的命名空间使得我们可以创建在一个文件范围里可用的命名空间。其一般形式如下:

  50. namespace

  51. {

  52.     //声明

  53. }

我们可以使用这种没有名称的命名空间创建只有在声明他的文件中才可见的标识符。也即是说,只有在声明这个命名空间的文件中,它的成员才是可见的,它的成员才是可以被直接使用的,不需要命名空间名称来修饰。对于其他文件,该命名空间是不可见的。我们在前面曾经提到过,把全局名称的作用域限制在声明他的文件的一种方式就是把它声明为静态的。尽管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++命名空间的解释的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1140565

相关文章

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

C++ vector的常见用法超详细讲解

《C++vector的常见用法超详细讲解》:本文主要介绍C++vector的常见用法,包括C++中vector容器的定义、初始化方法、访问元素、常用函数及其时间复杂度,通过代码介绍的非常详细,... 目录1、vector的定义2、vector常用初始化方法1、使编程用花括号直接赋值2、使用圆括号赋值3、ve

如何高效移除C++关联容器中的元素

《如何高效移除C++关联容器中的元素》关联容器和顺序容器有着很大不同,关联容器中的元素是按照关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的,本文介绍了如何高效移除C+... 目录一、简介二、移除给定位置的元素三、移除与特定键值等价的元素四、移除满足特android定条件的元

Python获取C++中返回的char*字段的两种思路

《Python获取C++中返回的char*字段的两种思路》有时候需要获取C++函数中返回来的不定长的char*字符串,本文小编为大家找到了两种解决问题的思路,感兴趣的小伙伴可以跟随小编一起学习一下... 有时候需要获取C++函数中返回来的不定长的char*字符串,目前我找到两种解决问题的思路,具体实现如下:

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

C/C++错误信息处理的常见方法及函数

《C/C++错误信息处理的常见方法及函数》C/C++是两种广泛使用的编程语言,特别是在系统编程、嵌入式开发以及高性能计算领域,:本文主要介绍C/C++错误信息处理的常见方法及函数,文中通过代码介绍... 目录前言1. errno 和 perror()示例:2. strerror()示例:3. perror(

C++变换迭代器使用方法小结

《C++变换迭代器使用方法小结》本文主要介绍了C++变换迭代器使用方法小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1、源码2、代码解析代码解析:transform_iterator1. transform_iterat

详解C++中类的大小决定因数

《详解C++中类的大小决定因数》类的大小受多个因素影响,主要包括成员变量、对齐方式、继承关系、虚函数表等,下面就来介绍一下,具有一定的参考价值,感兴趣的可以了解一下... 目录1. 非静态数据成员示例:2. 数据对齐(Padding)示例:3. 虚函数(vtable 指针)示例:4. 继承普通继承虚继承5.

C++中std::distance使用方法示例

《C++中std::distance使用方法示例》std::distance是C++标准库中的一个函数,用于计算两个迭代器之间的距离,本文主要介绍了C++中std::distance使用方法示例,具... 目录语法使用方式解释示例输出:其他说明:总结std::distance&n编程bsp;是 C++ 标准