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++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

Three.js构建一个 3D 商品展示空间完整实战项目

《Three.js构建一个3D商品展示空间完整实战项目》Three.js是一个强大的JavaScript库,专用于在Web浏览器中创建3D图形,:本文主要介绍Three.js构建一个3D商品展... 目录引言项目核心技术1. 项目架构与资源组织2. 多模型切换、交互热点绑定3. 移动端适配与帧率优化4. 可

GO语言中函数命名返回值的使用

《GO语言中函数命名返回值的使用》在Go语言中,函数可以为其返回值指定名称,这被称为命名返回值或命名返回参数,这种特性可以使代码更清晰,特别是在返回多个值时,感兴趣的可以了解一下... 目录基本语法函数命名返回特点代码示例命名特点基本语法func functionName(parameters) (nam

深入解析C++ 中std::map内存管理

《深入解析C++中std::map内存管理》文章详解C++std::map内存管理,指出clear()仅删除元素可能不释放底层内存,建议用swap()与空map交换以彻底释放,针对指针类型需手动de... 目录1️、基本清空std::map2️、使用 swap 彻底释放内存3️、map 中存储指针类型的对象

C++ STL-string类底层实现过程

《C++STL-string类底层实现过程》本文实现了一个简易的string类,涵盖动态数组存储、深拷贝机制、迭代器支持、容量调整、字符串修改、运算符重载等功能,模拟标准string核心特性,重点强... 目录实现框架一、默认成员函数1.默认构造函数2.构造函数3.拷贝构造函数(重点)4.赋值运算符重载函数

C++ vector越界问题的完整解决方案

《C++vector越界问题的完整解决方案》在C++开发中,std::vector作为最常用的动态数组容器,其便捷性与性能优势使其成为处理可变长度数据的首选,然而,数组越界访问始终是威胁程序稳定性的... 目录引言一、vector越界的底层原理与危害1.1 越界访问的本质原因1.2 越界访问的实际危害二、基

c++日志库log4cplus快速入门小结

《c++日志库log4cplus快速入门小结》文章浏览阅读1.1w次,点赞9次,收藏44次。本文介绍Log4cplus,一种适用于C++的线程安全日志记录API,提供灵活的日志管理和配置控制。文章涵盖... 目录简介日志等级配置文件使用关于初始化使用示例总结参考资料简介log4j 用于Java,log4c

C++归并排序代码实现示例代码

《C++归并排序代码实现示例代码》归并排序将待排序数组分成两个子数组,分别对这两个子数组进行排序,然后将排序好的子数组合并,得到排序后的数组,:本文主要介绍C++归并排序代码实现的相关资料,需要的... 目录1 算法核心思想2 代码实现3 算法时间复杂度1 算法核心思想归并排序是一种高效的排序方式,需要用

C++11范围for初始化列表auto decltype详解

《C++11范围for初始化列表autodecltype详解》C++11引入auto类型推导、decltype类型推断、统一列表初始化、范围for循环及智能指针,提升代码简洁性、类型安全与资源管理效... 目录C++11新特性1. 自动类型推导auto1.1 基本语法2. decltype3. 列表初始化3

C++11右值引用与Lambda表达式的使用

《C++11右值引用与Lambda表达式的使用》C++11引入右值引用,实现移动语义提升性能,支持资源转移与完美转发;同时引入Lambda表达式,简化匿名函数定义,通过捕获列表和参数列表灵活处理变量... 目录C++11新特性右值引用和移动语义左值 / 右值常见的左值和右值移动语义移动构造函数移动复制运算符