C++命名空间超级大详解

1 什么是命名空间?

  本质上,命名空间就是一个范围标识,比如大家都在中国,有中国湖北、中国北京、中国上海、中国香港、中国澳门、中国台湾,其中中国在程序中就是全局,湖北、北京、上海、香港、澳门、台湾,这些城市就是一个个命名空间 。

2 为什么要有命名空间

  之所以设计命名空间是为了解决命名冲突的问题,比如狮子山,全国有很多个狮子山,比如湖北狮子山、香港狮子山等等,那么怎样正确地标识这些重名的地方呢?答案就是使用命名空间,例如,湖北::狮子山,香港::狮子山。如果大家学习了操作系统,就会发现这和文件系统中多级目录的作用十分相似,都是为了解决命名冲突问题。

大型程序往往会使用多个独立开发的库,这些库又会定义大量的全局名字,如类、函数和模板等。当应用程序用到多个供应商提供的库时,不可避免地会发生某些名字相互冲突的情况。多个库将名字放置在全局命名空间中将引发命名空间污染(namespace pollution)
命名空间(namespace) 为防止名字冲突提供了更加可控的机制。命名空间分割了全局命名空间,其中每个命名空间都是一个作用域。通过在某个命名空间中定义库的名字,库的作者(以及用户)可以避免全局名字固有的限制。 --摘自《C++ Prime中文版(第五版)》

  大体上,一个C++程序在内存上可以分为堆、栈、全局区、只读常量区、代码区。

  1. 堆区,由程序员自己使用new delete之类的内存分配函数自己分配,自己释放。
  2. 栈区,由系统进行内存管理,主要存放函数的参数以及局部变量。
  3. 全局区,主要存放全局变量、静态变量,生命周期为程序的整个运行期间,作用范围是整个程序。
  4. 只读常量区,是用来存放常量的,只可以读取,不可修改。
  5. 代码区,存放程序体的二进制代码,只读

注:这只是大体的分区

命名空间可以被嵌套定义,但是只能定义在全局区,不能在堆、栈等其他区。

3 命名空间里有些什么

  1. 变量
  2. 函数
  3. 模板

头文件

#ifndef _NAMESPACE1_H
#define _NAMESPACE1_H
namespace QZQ1 {
	extern int age;
	extern  char* name;
	void QzqPrint();
	template<typename T>void swap(T& a, T& b) {
		T c = a;
		a = b;
		b = c;
	}
	class A {
		public:
		int a;
		int b;
	};
};
#endif 

cpp文件

#include "namespace1.h"
#include <iostream>

namespace QZQ1 {
   int age;
   char* name;
   void QzqPrint() {
   	std::cout << "hello,word" << std::endl;
   }
};

这一块我找了很久,没有找到明确的说明,但是上述皆可在命名空间中定义。

4 怎样创建一个命名空间

1. 创建方法

namespace  命名空间名字{
	//空间内容
};

例如:

namespace pig{
 	int flag;
	struct Information* GetInformation();
	void KillPig(int num);
};

注:注意区分命名空间和结构体的区别,别用混了。

2. 头文件和cpp文件

  在命名空间中,必须把声明写在头文件中,大部分情况下必须把定义写在cpp文件中,否则当头文件被多次包含后就会出现重定义的错误。遇到定义和声明无法分离的东西,比如模板,就直接写在头文件中。

头文件

#ifndef _NAMESPACE1_H
#define _NAMESPACE1_H
namespace QZQ1 {
	extern int age;
	extern  char* name;
	void QzqPrint();
	template<typename T>void swap(T& a, T& b) {
		T c = a;
		a = b;
		b = c;
	}
};
#endif 

cpp文件

#include "namespace1.h"
#include <iostream>

namespace QZQ1 {
   int age;
   char* name;
   void QzqPrint() {
   	std::cout << "hello,word" << std::endl;
   }
};

3.命名空间的定义可以不连续

命名空间可以定义在几个不同的部分
头文件

#ifndef _NAMESPACE1_H
#define _NAMESPACE1_H
namespace QZQ1 {
	extern int p1;
	extern int p2;
};
#endif 

cpp文件

#include "namespace1.h"
#include <iostream>

namespace QZQ1 {
	int p1;
};

namespace QZQ1 {
	int p2;
};

4. 命名空间可以嵌套定义

namespace A {
	namespace B1 {
		int a;
	};
	namespace B2 {
		int a;
	};
	int a;
};

5. 未命名的命名空间

namespace {
	int a;
	int b;
};

未命名的命名空间是为了替代之前C语言的静态变量而出现的,以前C语言用静态变量,现在在C++中,使用未命名的命名空间来替代即可。未命名的命名空间仅在本文件中有效,每一个文件只有一个未命名的命名空间,处于顶层的未命名的命名空间中的变量不可以和外部的全局变量同名,如下所示就是错误的:

int a;
namespace {
	int a;
};

未命名的命名空间也是可以嵌套定义的,如下所示

namespace {
	namespace {
		int a;
	}
};

这种嵌套只是方便逻辑上地规划,并没有真正地物理上嵌套,要注意地是,未命名嵌套中非顶层的命名空间中的元素依旧不能和外部的变量重名,如下所示是错误的:

int a;
namespace {
	namespace {
		int a;
	}
};

5 怎样使用命名空间

1. 通过命名空间名使用

namespace pig{
   int flag;
   int GetAge(int num) {
   	return 2 * num;
   }
};
int main() {
   pig::GetAge(10);
   return 0;
}

2. 通过using方法使用

使用using有两种用法,一种是直接using namespace::SpaceName,称为using指示,之后就可以直接使用命名空间内的成员了,如下所示:

using namespace std;
using namespace pig;
int main() {
   flag = 1;
   cout << flag << " " << GetAge(10) << endl;
   return 0;
}

第二中using用法是using SpaceName::成员,称为using声明,之后就可以直接使用对应的成员了,不过其他的成员依旧需要加命名空间名字做前缀,如下所示:

using  std::cout;
using pig::flag;
int main() {
	flag = 1;
	cout << flag << " " << pig::GetAge(10) << std::endl;
	return 0;
}

需要好好理解using指示和using声明,要慎用using指示,应为using指示比较容易造成命名冲突

3. 别名

namespace me = pig;

别名的作用就是用较短的名字来替代较长的名字,并没有特别的含义。别名与原来的命名空间等价。

4. 注意事项

  • 不要在头文件中使用using namespace SpaceName,即using指示,这样操作的话,如果头文件被多次包含,则命名冲突可能性会大大提高。头文件中,可以选择使用using声明。
发布了33 篇原创文章 · 获赞 47 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_39545674/article/details/103755433