C++ 【命名空间-“using namespace std“】

 

 

命名空间

学习c++的时候,看到很多程序中用了以下语句:using namespace std;这表明使用了命名空间std,那么什么是命名空间?为什么要用命名空间?本文将详细介绍命名空间的意义和用法。

​ 首先大型应用程序经常使用来自不同厂商的开发库,几乎不可避免会使用相同的名字,也就是说一个库中定义的名字可能与其他库中的名字相同而产生冲突。假如不同的程序员分别定义了类和变量,放在了不同的头文件中,在主函数的文件中需要使用这些类时,就用#include指令将这些头文件包含进来。由于头文件是由不同的人设计的,不同头文件中可能用了相同的名字来命名所定义的类或函数。在预编译后,头文件中的内容取代了对应的#include指令,这样就在同一个程序文件中出现了多个名字相同的类或变量,显然是重复定义,这就是名字冲突,即在同一个作用域中有两个或者多个同名的实体。在使得程序员不能组合各自独立的开发库到一个程序中。

​ 对此ANSI C++引入的可以由用户命名的作用域,用来处理程序中常见的同名冲突。命名空间是用来限定名字的解析和使用范围,它是C++开发大型程序的工具之一。命名空间的原理是将全局作用域划分为一个一个的命名空间,每个命名空间是一个独立的作用域,在不同命名空间内部定义的名字彼此之间互不影响,从而有效的避免了命名污染。

1.命名空间定义

在C语言中只有一个全局作用域

  • 所有的全局标识符共享一个作用域
  • 这使得标识符之间可能发生冲突。

C++中提出了命名空间的概念

  • 命名空间将全局作用域分成不同的部分
  • 不同命名空间中的标识符可以同名而不会发生冲突
  • 命名空间可以发生嵌套
  • 全局作用域也叫默认命名空间

命名空间的定义形式namespace 命名空间名 { …… } ;

使用命名空间形式using namespace 命名空间;或者命名空间::成员名

命名空间可以在全局作用域或其他命名空间内部定义,但不能在函数、结构体或类内部定义,且要保证命名空间之间不会出现名字冲突。

在命名空间作用域内,可以包含: 变量、对象以及它们的初始化、枚举常量、函数声明以及函数定义、类、结构体声明与实现、模板、其他命名空间

namespace A
{ //定义命名空间
    A const int PI=3.1415926; //const常量 
    enum tagDAYS{MON,TUE,WED,THU,FRI,SAT,SUN}; //枚举常量 
    int i,j,k=10; //变量及变量的初始化 
    string str,str2("hello"); //对象及对象的初始化 
    int max(int x,int y); //函数声明 
    int min(int x,int y){return x>y?x:y;} //函数定义 
    template<typename T> //函数模板 
    int compare(const T&v1,const T&v2){return v1==v2;} 
    template<class T> class TComplex
    {     //类模板 
        public: TComplex(){} void setdata(T a,T b){x=a;y=b;} 
        private: T r,i; 
    };
    namespace B{ //嵌套的命名空间 int i,j,k; } 
}

2.命名空间的使用

  1. 每个命名空间是一个作用域,定义在命名空间中的实体称为命名空间成员。命名空间中的每个名字必须是该命名空间中的唯一实体,不同命名空间可以具有同名成员。

    namespace A
    {   //定义命名空间A
    	int fun,j;
    	int fun(){return 10;}//错误,同一命名空间不能有相同的名字
    } 
    namespace B
    {   //定义命名空间B
    	int fun,j; //正确,不同命名空间可以有相同的名字
    }
    
  2. 在命名空间中定义的名字可以被命名空间中的其他成员直接访问, 命名空间外部的代码必须指出名字定义在哪个命名空间中,即作用域运算符限定命名空间,形式为:命名空间::成员名

    namespace A
    {   //定义命名空间A
    	int i=10;
    	void output1(){cout<<i;} //同一命名空间种直接引用成员i
    } 
    void output2(){cout<<A::i;} //引用命名空间限定成员A::i
    
  3. 命名空间可以是不连续的,命名空间可以在几个部分中分开定义,即命名空间是累积的。一个命名空间可以分散在多个文件中。

    namespace A{   
        //新创建一个命名空间A
    	int i;
    } 
    namespace B{   
        //新创建一个命名空间B
    	int i,j; 
    }
    namespace A{   
        //合并到前面的命名空间A
    	int j;
    } 
    
  4. 接口和实现分离。命名空间可以不连续意味着可以做以下事情:命名空间成员如果是类,可以将作为类接口的函数声明和对象声明放在头文件中,使用这些命名空间成员的文件可以包含这些头文件。命名空间类成员的定义可以放在单独的源文件中。

    //complex.h 类接口放在头文件中
    namespace A
    { //定义命名空间A
    	class complex{
    	public:
    	complex(){}
    	void setdata(double a,double b);
    	private:
    	double r,i;};
    } 
    //complex.cpp 类实现放在源文件中
    namespace A{ //合并到头文件中的A
    void complex::setdata(double a,double b){r=a;i=b;} 
    }
    
  5. 在命名空间内部定义的函数可以使用同一命名空间中定义的名字。(命名空间是累积的)

    namespace A
    {   //sub中直接使用complex类型
    	complex sub(complex& c1,complex& c2);
    }
    
  6. 也可以在命名空间外部定义命名空间成员:

    A::complex A::sub(complex& c1,complex& c2);
    { 
        complex c;
    	c.setdata(c1.getr()+c2.getr(),c1.geti()+c2.geti());
    	return c;
    }
    
  7. 定义在全局作用域的名字(任何类、函数或命名空间外部声明的名字)是定义在全局命名空间中的。全局命名空间存在于每个程序中,是隐式的。可以用作用域运算符引用全局命名空间的成员

    int i=10; //全局作用域
    namespace A
    { 	//命名空间A
    	void output(){ cout<<::i; } //使用全局作用域成员i
    }
    
  8. 嵌套命名空间中的名字遵循名字规则:外围命名空间中声明的名字被嵌套命名空间中同一名字的声明所屏蔽。(类似类中的隐藏)

    namespace outer
    { 	//外围命名空间
    	int i; 
    	namespace inner
    	{ 	//嵌套命名空间
    		void f(){i++;} //使用的是outer::i
    		int i; //嵌套命名空间中的i屏蔽了outer::i
    		void g(){i++;} //使用的是inner::i
    		void h(){outer::i++;} //使用的是outer::i
    	}
    } 
    
  9. 定义命名空间时如果没有给出命名空间名字,称为未命名的命名空间,定义形式为**namespace { …… }**。

    • 未命名的命名空间中定义的名字可以直接使用,没有命名空间名字来限定它们。

    • 未命名的命名空间可以在给定文件中不连续,但不能跨越文 件,每个文件有自己的未命名的命名间。本质上在一个文件中所有未命名的命名空间会被系统用同一个标识符代替,且区别于其他文件的标识符。

    • 未命名的命名空间专门用于声明局部于文件的实体。

    • 未命名的命名空间中定义的名字只在包含该命名空间的文件中可见。如果另一文件包含一个未命名的命名空间,两个命名空间不相关,可以定义相同名字的实体。

namespace { int i; } //假定未命名的命名空间标识为unique 
void f(){ i++; } //unique::i++ 
namespace A
{ 
    namespace 
    { 
        int i,j; //A::unique::i和A::unique::j 
    }
    void g(){ i++; } //A::unique::i++ } 
    using namespace A; 
    void h()
    { 
        A::i++; //A::unique::i++ 
        j++; //A::unique::j++ 
    }
}

3.using namespace std;的作用

首先using namespace std的意思是:打开标准命名空间,即告诉编辑器我们将要使用名字空间std中的函数或者对象。

  • using是正在使用的意思。
  • namespace 的引用是为了解决不同space中命名相同导致命名冲突的问题。使用using namespace +命名空间能让编译器准确的找到相应的函数或者对象,有效的提高程序员写代码的效率,但这些都和性能无关,仅仅对程序员有利。
  • std 是个名称空间标识符C++标准库中的函数或者对象都是在命名空间std中定义的,像cin,cout是预定义在std命名空间的一种类对象名,只有在std命名空间下,编译器才能正常执行输入输出作用。所以我们要使用标准库中的函数或者对象都要用std::来限定。(std::成员名意思就是使用std名字空间下定义好的函数和对象

只是在std 标准空间里,包含了原来的库和头文件。但是在C++ 中因为我们要使用的STL中有部分名称是没有加下划线的保留标记的,而这些名称如果再用在自己的源代码中就会引发未定义的后果。所以当我们在写成熟的代码的时候,不建议将标准命名空间全部打开,而是需要用库里的什么就打开什么。这就有效的防止了命名冲突。使用命名空间有三种方式:(提倡用第三种

//第一种使用:使用using namespace std; 完全打开std 命名空间 --不安全,超简单
#include <iostream>
using namespace std;        
int main()
{...}
//第二种使用:提前打开需要使用的std 中对象/函数 --安全,推荐
#include <iostream>
using std::string;   //   提前打开需要的名字
using std::cout;                
using std::endl;  
using std::cin;  //没有打开会报错
int main()
{...}
//第三种使用:什么时候用什么时候打开std 中对象/函数 --安全,较复杂(提倡)
#include <iostream>
int main()
{
	std::string a;
	std::cin >> a;
//	cin >> a;       //没有打开,会报错
	std::cout<<a<<std::endl;     //运行结果与上面相同
//  cout << a;   //第二次使用时,仍然需要再次打开,否则报错
   std::cout<<a;   //运行结果多一个hello,但没有换行
	return 0;
}

4.为什么要少用using namespace

首先using namespace std;使用的弊端上面也已经说过,下面谈自己命名的名字空间为什么也不要用using namespace

using namespace A;
using namespace B;

现在使用A中的SeqListPushBack()函数,也可以使用B中的SeqListPopFront()函数。但如果以后你升级了A命名空间,也加入了一个函数SeqListPopFront()函数,那么这时候,你原来的代码就出问题了,是哪里的SeqListPopFront()函数?如果是个大工程,这得就是自己给自己埋坑!

5. 如何用命名空间避免命名冲突?

首先包含头文件!但是正如第四点所说的,不用using namespace!使用变量的时候命名空间::变量名,指定使用的是呢一个命名空间的变量名。虽然繁杂,但是规范!

 

 

 

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇

)">
下一篇>>