`

命名空间(一)

 
阅读更多

一、 为什么需要命名空间(问题提出)——同一作用域中,相同名字发生冲突


    命名空间是ANSIC++引入的可以由用户命名的作用域,用来处理程序中 常见的同名冲突。
   在 C语言中定义了3个层次的作用域,即文件(编译单元)、函数和复合语句。C++又引入了类作用域,类是出现在文件内的。在不同的作用域中可以定义相同名字的变量,互不于扰,系统能够区别它们。


    1、全局变量的作用域是整个程序,在同一作用域中不应有两个或多个同名的实体(enuty),包括变量、函数和类等。
例:如果在文件中定义了两个类,在这两个类中可以有同名的函数。在引用时,为了区别,应该加上类名作为限定:

#include <iostream>
using namespace std;

class A{
public:
       void fun1();
};
void A::fun1(){    cout<<"A::fun1()"<<endl;}

class B{
public:
       void fun1();
       void fun2();
};
void B::fun1(){    cout<<"B::fun1()"<<endl;}
void B::fun2(){    cout<<"B::fun2()"<<endl;}

int main(){
    A a;
    B b;
    a.fun1();
    b.fun1();
    b.fun2();
    system("pause");
    return 0;
}

这样不会发生混淆。
    在文件中可以定义全局变量(global variable),它的作用域是整个程序。如果在文件A中定义了一个变量a       

int a=3;在文件B中可以再定义一个变量a       int a=5;
    在分别对文件A和文件B进行编译时不会有问题。但是,如果一个程序包括文件A和文件B,那么在进行连接时,会报告出错,因为在同一个程序中有两个同名的变量,认为是对变量的重复定义。

 

解决方法:
   可以通过extern声明同一程序中的两个文件中的同名变量是同一个变量。如果在文件B中有以下声明:
      extem int a;
   表示文件B中的变量a是在其他文件中已定义的变量。由于有此声明,在程序编译和连接后,文件A的变量a的作用域扩展到了文件B。如果在文件B中不再对a赋值,则在文件B中用以下语句输出的是文件A中变量a的值: cout<<a; //得到a的值为3

 

2、程序中出现名字冲突

   在简单的程序设计中,只要人们小心注意,可以争取不发生错误。但是,一个大型的应用软件,往往不是由一个人独立完成的,而是由若干人合作完成的,不同的人分别完成不同的部分,最后组合成一个完整的程序。假如不同的人分别定义了类,放在不同的头文件中,在主文件(包含主函数的文件)需要用这些类时,就用#include命令行将这些头文件包含进来。由于各头文件是由不同的人设计的,有可能在不同的头文件中用了相同的名字来命名所定义的类或函数。

例4 名字冲突
    程序员甲在头文件headerl.h中定义了类 Student和函数fun。

// 头文件headerl.h
#include <string>
#include <cmath>
using namespace std;

//声明Student类
class Student{
public:
     Student(int n,string nam,int a){ num=n;name=nam;age=a;}
     void get_data();
private:
     int num;
     string name;
     int age;
};

//成员函数定义
void Student::get_data(){
     cout<<num<<" "<<name<<" "<<age<<endl;
}

//定义全局函数(即外部函数)
double fun(double a,double b){
     return sqrt(a+b);
}
 

在main函数所在的文件中包含头文件headerl.h:

#include <iostream>
using namespace std;

//注意要用双引号,因为文件一般是放在用用户目录中的
#include "header1.h" 

int main(){
    Student stud1(101,"Wang",18); //定义类对象studl
    stud1.get_data();
    cout<<fun(5,3)<<endl;
    system("pause");
    return 0; 
}

 

程序 能正常运行,输出为


   如果程序员乙写了头文件header2.h,在其中除了定义其他类以外,还定义了类Student和函数fun,但其内容与头文件headerl.h中的 Student和函数fun有所不同。

//header2.h
#include <string>
#include <cmath>
using namespace std;

//声明Student类
class Student{
public:
     //参数与headerl中的student不同
     Student(int n,string nam,char s){ num=n;name=nam;sex=s;}
     void get_data();
private:
     int num;
     string name;
     char sex; //此项与headerl不同, header1中是int age 
};

//成员函数定义
void Student::get_data(){
     cout<<num<<" "<<name<<" "<<sex<<endl; 
}

//定义全局函数
double fun(double a,double b){
       return sqrt(a-b);   //header1是 sqrt(a+b) 
}

//header2.h中还有其他内容(需要被main用到)

 

   假如主程序员在其程序中要用到headerl.h中的Student和函数fun,因而在程序中包含了头文件headerl.h,同时要用到头文件 header2.h中的一些内容(但对header2.h中包含与headerl.h中的Student类和fun函数同名而内容不同的类和函数并不知情,因为在一个头文件中往往包含许多不同的信息,而使用者往往只关心自己所需要的部分,而不注意其他内容),因而在程序中又包含了头文件 header2.h。如果主文件(包含主函数的文件)如下:

#include <iostream>
using namespace std;

#include "header1.h"
#include "header2.h"

int main(){
    Student stud1(101,"Wang",18);
    stud1.get_data();
    cout<<fun(5,3)<<endl;
    system("pause");
    return 0; 
}

   这时程序编译就会出错。因为在预编译后,头文件中的内容取代了对应的#include命令行,这样就在同一个程序文件中出现了两个Student类和两个fun函数,显然是重复定义,这就是名字冲突,即在同一个作用域中有两个或多个同名的实体。

   3、全局命名空间污染(global namespace pollution)。


   在程序中还往往需要引用一些库(包括C++编译系统提供的库、由软件开发商提供的库或者用户自己开发的库),为此需要包含有关的头文件。如果在这些库中包含有与程序的全局实体同名的实体,或者不同的库中有相同的实体名,则在编译时就会出现名字冲突。
   为了避免这类问题的出现,人们提出了许多方法,例如:将实体的名字写得长—些(包含十几个或几十个字母和字符);把名字起得特殊一些,包括一些特殊的字符;由编译系统提供的内部全局标识符都用下划线作为前缀,如_complex(),以避免与用户命名的实体同名;由软件开发商提供的实体的名字用特定的字符作为前缀。但是这样的效果并不理想,而且增加了阅读程序的难度,可读性降低了。
   C 语言和早期的C++语言没有提供有效的机制来解决这个问题,没有使库的提供者能够建立自己的命名空间的工具。人们希望ANSI C++标准能够解决这个问题,提供—种机制、一种工具,使由库的设计者命名的全局标识符能够和程序的全局实体名以及其他库的全局标识符区别开来。

 

二、 什么是命名空间(解 决方案)

   命名空间 :实际上就是一个由程序设计者命名的内存区域,程序设计者可以根据需要指定一些有名字的空间域,把一些全局实体分别放在各个命名空间中,从而与其他全局实体分隔开来。

如:

namespace ns1{
          int a;
          double b; 
}

   namespace 是定义命名空间所必须写的关键字,nsl 是用户自己指定的命名空间的名字(可以用任意的合法标识符,这里用ns1是因为ns是namespace的缩写,含义清楚),在花括号内是声明块,在其中声明的实体称为命名空间成员(namespace member) 。现在命名空间成员包括变量a和b,注意a和b仍然是全局变量,仅仅是把它们隐藏在指定的命名空间中而已 。如果在程序中要使用变量a和b,必须加上命名空间名和作用域分辨符“::”,如nsl::a,nsl::b。这种用法称为命名空间限定 (qualified),这些名字(如nsl::a)称为被限定名 (qualified name)。

   C++中命名空间的作用类似于操作系统中的目录和文件的关系,由于文件很多,不便管理,而且容易重名,于是人们设立若干子目录,把文件分别放到不同的子目录中,不同子目录中的文件可以同名。调用文件时应指出文件路径。

   命名空间的作用:是建立一些互相分隔的作用域,把一些全局实体分隔开来。 以免产生老点名叫李相国时,3个人都站起来应答,这就是名字冲突,因为他们无法辨别老师想叫的是哪一个李相国,同名者无法互相区分。为了避免同名混淆,学校把3个同名的学生分在3个班。这样,在小班点名叫李相国时,只会有一个人应答。也就是说,在该班的范围(即班作用域)内名字是惟一的。如果在全校集合时校长点名,需要在全校范围内找这个学生,就需要考虑作用域问题。如果校长叫李相国,全校学生中又会有3人一齐喊“到”,因为在同一作用域中存在3个同名学生。为了在全校范围内区分这3名学生,校长必须在名字前加上班号,如高三甲班的李相国,或高三乙班的李相国,即加上班名限定。这样就不致产生混淆。

   可以根据需要设置许多个命名空间,每个命名空间名代表一个不同的命名空间域 ,不同的命名空间不能同名。这样,可以把不同的库中的实体放到不同的命名空间中,或者说,用不同的命名空间把不同的实体隐蔽起来。过去我们用的全局变量可以理解为全局命名空间 ,独立于所有有名的命名空间之外,它是不需要用 namespace声明的,实际上是由系统隐式声明的,存在于每个程序之中。

   在声明一个命名空间时,花括号内不仅可以包括变量,而且还可以包括以下类型:
     ·变量(可以带有初始化);
     ·常量;
     ·数(可以是定义或声明);
     ·结构体;
     ·类;
     ·模板;
     ·命名空间(在一个命名空间中又定义一个命名空间,即嵌套的命名空间)。

例如

namespace nsl{
   const int RATE=0.08; //常量
   doublepay;       //变量
   doubletax(){return a*RATE;} //函数 
   namespacens2{//嵌套的命名空间
      int age;
   }
}

 

   如果想输出命名空间nsl中成员的数据,可以采用下面的方法:

cout<<nsl::RATE<<endl;
cout<<nsl::pay<<endl;
cout<<nsl::tax()<<endl;
cout<<nsl::ns2::age<<endl; //需要指定外层的和内层的命名中间名

   可以看到命名空间的声明方法和使用方法与类差不多。但它们之间有一点差别:在声明类时在右花括号的后面有一分号,而在定义命名空间时,花括号的后面没有分号

 

三、 使用命名空间解决名字冲突(使用指南)

   有了以上的基础后,就可以利用命名空间来解决名字冲突问题。现在,对上面冲突的程序进行修改,使之能正确运行。
   修改两个头文件,把在头文件中声明的类分别放在两个不同的命名空间中。

 

修改后的header1.h :

// 修改后头文件headerl.h
#include <string>
#include <cmath>
using namespace std;

namespace ns1{
//声明Student类
class Student{
public:
     Student(int n,string nam,int a){ num=n;name=nam;age=a;}
     void get_data();
private:
     int num;
     string name;
     int age;
};

//成员函数定义
void Student::get_data(){
     cout<<num<<" "<<name<<" "<<age<<endl;
}

//定义全局函数(即外部函数)
double fun(double a,double b){
     return sqrt(a+b);
}

}

 

修改后的header2.h :

//修改后头文件header2.h
#include <string>
#include <cmath>
using namespace std;

namespace ns2{

//声明Student类
class Student{
public:
     //参数与headerl中的student不同
     Student(int n,string nam,char s){ num=n;name=nam;sex=s;}
     void get_data();
private:
     int num;
     string name;
     char sex; //此项与headerl不同, header1中是int age 
};

//成员函数定义
void Student::get_data(){
     cout<<num<<" "<<name<<" "<<sex<<endl; 
}

//定义全局函数
double fun(double a,double b){
       return sqrt(a-b);   //header1是 sqrt(a+b) 
}

}
 

修改后的主程序:

#include <iostream>
using namespace std;

#include "header1.h"
#include "header2.h"

int main(){
    ns1::Student stud1(101,"Wang",18);
    stud1.get_data();
    cout<<ns1::fun(5,3)<<endl;
    
    ns2::Student stud2(102,"Li",'f');
    stud2.get_data();
    cout<<ns2::fun(5,3)<<endl;
    
    system("pause");
    return 0; 
}

 

   解决本题的关键是建立了两个命名空间nsl和ns2,将原来在两个头文件中声叫的类分别放在命名空间nsl和ns2中。注意:在头文件中,不要把#include命令放在命名空间中,在上一小节的叙述中可以知道,命名空间中的内容不包括命令行,否则编译会出错。
   分析以前程序出错的原因是:在两个头文件中有相同的类名Student和相同的函数名fun,在把它们包含在主文件中时,就产生名字冲突,存在重复定义。编译系统无法辨别用哪一个头文件中的Student来定义对象。现在两个Student和fun分别放在不同的命名空间中,各自有其作用域,互不相干。由于作用域不相同,不会产生名字冲突。正如同在两个不同的类中可以有同名的变量和函数而不会产生冲突一样。
   在定义对象时用ns1::Student(命名空间nsl中的Student)来定义stud1,用ns2::Student(命名空间ns2中的 Student)来定义stud2。显然,ns1::Student和ns2::Student是两个不同的类,不会产生混淆。同样,在调用fun函数时也需要用命名空间名ns1或ns2加以限定。ns1::fun()和ns2::fun()是两个不同的函数。注意:对象stud1是用 ns1::Student定义的,但对象stud1并不在命名空间ns1中。stud1的作用域为main函数范围内。在调用对象stud1的成员函数 get_data时,应写成stud1.get_data(),而不应写成ns1::studl.get_data()。
   程序能顺利通过编译,并得到以下运行结果:


 

 

四、 使用命名空间成员的方法

   从上面的介绍可以知道,在引用命名空间成员时,要用命名空间名和作用域分辨符对命名空间成员进行限定,以区别不同的命名空间中的同名标识符。即:
   命名空间名::命名空间成员名
   这种方法是有效的,能保证所引用的实体有惟一的名字。但是如果命名空间名字比较长,尤其在有命名空间嵌套的情况下,为引用一个实体,需要写很长的名字。在一个程序中可能要多次引用命名空间成员,就会感到很不方便。

1、使用命名空间别名(略)

2、using ns1:命名空间成员

using声明的有效范围是从using语句开始到using所在的作用域结束。
(1)如果在以上的using语句之后有以下语句:
Student studl(101,"Wang",18); //此处的Student相当于ns1::Student
上面的语句相当于
nsl::Student studl(101,"Wang",18);
(2)又如,
using nsl::fun; //声明其后出现的fun是属于命名空间nsl中的fun
cout<<fun(5,3)<<endl;//此处的fun函数相当于nsl::fun(5,3)
显然,这可以避免在每一次引用命名空间成员时都用命名空间限定,使得引用命名空间成员变得方便易用。
但是要注意:在同一作用域中用using声明的不同命名空间的成员中不能有同名的成员。

3、using namespace 命名空间名(略)

 

参考文档:

http://blog.csdn.net/liufei_learning/article/details/5391334

  • 大小: 15.5 KB
  • 大小: 20.2 KB
分享到:
评论

相关推荐

    PHP命名空间的使用,PHP命名空间实例

    在PHP编程中,命名空间(Namespace)是一种组织代码的重要机制,它允许我们在同一个全局命名空间内定义具有相同名称的不同函数、类或接口,避免了命名冲突的问题。本篇将深入探讨PHP命名空间的使用和实例。 一、PHP...

    c++ 命名空间

    #### 一、命名空间概述 在C++中,命名空间(`namespace`)是一种用于组织标识符的方式,它能够有效地解决标识符的命名冲突问题。C++标准库中的所有标识符均被定义在一个名为`std`的命名空间中。这种做法的主要目的...

    Qt命名空间 Qt namespace

    Qt命名空间是Qt库中的一个重要概念,它是C++编程中的一种组织代码的方式,用于避免不同模块或库之间的名称冲突。在Qt中,大部分类都属于`Qt`命名空间,这使得开发者可以清楚地知道哪些函数和类是Qt提供的。本文将...

    C#自定义命名空间与使用自定义的命名空间方法

    #### 一、理解命名空间在C#中的作用 在C#中,命名空间(Namespace)是一种组织代码的方法,它帮助开发者避免命名冲突,使代码结构更加清晰。通过命名空间,可以将相关的类、接口、枚举等类型组织在一起,形成逻辑上...

    php命名空间小结

    命名空间在PHP中是一个至关重要的概念,自PHP 5.3版本引入,它提供了一种组织和管理代码的方式,有效地解决了代码中可能出现的命名冲突问题。通过命名空间,我们可以将相关的类、接口、函数和常量分组,类似于操作...

    linux命名空间的介绍

    每个IPC命名空间可以包含一组独立的IPC对象,这些对象对于该命名空间内的进程是可见的,而对于其他命名空间内的进程则是不可见的。 #### IPC命名空间的关键功能: 1. **信号量、消息队列和共享内存的隔离**:每个...

    小组学习:C#命名空间

    在C#编程语言中,命名空间(Namespace)是组织代码的一种关键方式,它允许我们将相关的类、接口和其他类型集合在一起,以便于管理和使用。在"小组学习:C#命名空间"这个主题中,我们将深入探讨命名空间的概念、作用...

    C#命名空间详解

    命名空间通过限定符(即命名空间名称加上类型名称)来唯一标识一个类型,从而解决了这一问题。 #### 使用命名空间 在C#中,使用命名空间非常简单,只需要在代码文件的顶部声明命名空间即可。例如: ```csharp ...

    protobuf中的命名空间使用demo(C++)

    在C++编程中,protobuf提供了一种类似C++命名空间的机制,被称为“包”(package),用于组织和管理.proto文件中的消息类型和其他定义,避免了全局命名冲突的问题。 在protobuf中,命名空间主要通过在.proto文件中...

    WebApi返回xml格式时去除命名空间

    默认情况下,WebAPI生成的XML数据会包含命名空间信息,这可能导致客户端处理数据时的不便。本文将深入探讨如何在C#中使用WebAPI去除返回XML数据时的命名空间。 首先,我们需要了解XML命名空间的作用。XML命名空间是...

    命名空间 Linux

    目前Linux实现了六种类型的namespace 每一个namespace是包装了一些全局系统资源的抽象集合 这一抽象集合使得在进程的命名空间中可以看到全局系统资源 命名空间的一个总体目标是支持轻量级虚拟化工具container的实现 ...

    Silverlight命名空间

    assembly=AAALibrary"`定义了一个新的命名空间前缀`a`,并将其关联到`AAA`命名空间下的`AAALibrary`程序集。`clr-namespace`指示这是.NET的命名空间,而`assembly`则指明了该命名空间所在的程序集位置。 在...

    .net命名空间解释列表

    它包含了大量的类库,这些类库被组织成一系列的命名空间,便于开发者按照功能或领域进行代码的组织和引用。命名空间在.NET编程中扮演着至关重要的角色,它们提供了模块化的代码结构,避免了命名冲突,并且使得代码的...

    修改c#类的命名空间名称

    内容概要:能够批量修改c#语言,类的命名空间。可以检测文件夹下命名空间名称是否相同,不同的命名空间名称通过树状结构显示。 适合人群:多应用于合并项目、拷贝项目...使用建议:建议只修改一个文件夹内的命名空间。

    .net命名空间介绍

    在 .NET 框架中,命名空间是一种组织类型的方式,可以避免名称冲突并使代码结构更加清晰。本文将详细介绍 .NET 2.0 中的一些关键命名空间及其作用。 #### Microsoft.CSharp 这个命名空间支持 C# 编译器的功能。它...

    dom4j 读取带有命名空间的xml文件

    在处理带有命名空间的XML文件时,DOM4J库显得尤为重要,因为XML命名空间是XML规范中一个关键的概念,用于避免元素名称的冲突。 XML命名空间的引入是为了在一个文档中使用来自不同来源的元素和属性。它们通过URI...

    .net 命名空间总结

    在.NET Framework或.NET Core/Standard中,命名空间(Namespace)是组织类和其他类型的一种逻辑分组方式,旨在帮助开发人员管理和避免名称冲突。本篇文章将深入探讨.NET中的命名空间,并通过一系列具体的示例来解释...

    SystemDataLinq命名空间问题解决

    总的来说,`System.Data.Linq`命名空间是.NET框架中一个强大的工具,它简化了数据库操作,使得开发者可以使用更直观、类型安全的代码与SQL数据库进行交互。正确添加引用后,你就能充分利用它的功能,提高开发效率。

    C#基础知识\Visual C#命名空间详解.doc

    在C#编程语言中,命名空间(Namespace)是组织代码的一种关键机制,它允许开发者将相关的类、结构、接口等类型归类到一起,形成逻辑上的分组,从而提高代码的可读性和可维护性。在文档"C#基础知识\Visual C#命名空间...

Global site tag (gtag.js) - Google Analytics