初步认识c++之命名空间详解(千字长文带你刨析你命名空间的细节)

时间:2022-11-05 13:00:02

初识c++之命名空间详解

前言:c++是在c语言的基础上进一步发展出来的用于弥补c语言缺陷的语言,是c语言的超集,添加了很多新的特性,使其编程更加方便!下面就让我们开始初步认识c++吧!

1.命名空间 :namespace

我们在c语言的编程中经常出现一个问题——当我们的==变量名/函数名/类型==不小心重复命名的时候,或者==不小心使用了与库中函数相同的名字==当做变量名,那么就会出现编译报错!

初步认识c++之命名空间详解(千字长文带你刨析你命名空间的细节)

初步认识c++之命名空间详解(千字长文带你刨析你命名空间的细节)

这很不方便,当我们多人合作写一个大型的项目的时候,就很容易出现这种问题,c++为此引入了namespace(命名空间)来解决这个问题

当我们使用命名空间的时候

初步认识c++之命名空间详解(千字长文带你刨析你命名空间的细节)

就会发现原本的报错就消失了!这就是命名空间的功能:==影响编译查找规则==!

1.1域作用限定符 ::

在详细搞懂namespace之前我们要先了解域作用限定符 ==::==

这个符号有什么作用呢?

#include <stdio.h>
int a = 10;
int main()
{
    int a = 0;
    printf("%d",a);
}

初步认识c++之命名空间详解(千字长文带你刨析你命名空间的细节)

上面的程序我们可以看如果进行打印,打印出来的一定会是0 ,因为局部优先原则,当局部和全局都存在相同变量名的时候,局部变量会优先选择!但是如果我们一定要打印全局变量a而不是局部变量a呢?且不对局部变量进行删除!

这时候就要用到我们的域作用限定符了!

#include <stdio.h>
int a = 10;
int main()
{
    int a = 0;
    printf("%d",::a);
}

初步认识c++之命名空间详解(千字长文带你刨析你命名空间的细节)

我们可以看到我们成功的打印出了全局变量a!

==当::左边为空的时候就是默认为全局域==,而且我们可以看出其实编译的时候默认遵循一套查找规则先找局部,再找全局,局部没有了,才回去全局找!但是域作用限定符可以改变这个规则,让它去我们想要的域去寻找变量!

1.2命名空间的本质

命名空间就相当于是创建了一个域,通过域作用限定符让程序在指定的域中查找我们在命名空间里面定义的变量/函数/类型!==这相当于就是影响了编译查找规则!记住程序也只会在这个域中进行查找!不会说这个域找不到就去全局,局部找!如果这个域没有你所定义的变量/函数/类型!就会编译报错!==

初步认识c++之命名空间详解(千字长文带你刨析你命名空间的细节)

初步认识c++之命名空间详解(千字长文带你刨析你命名空间的细节)

1.2.1命名空间中的变量!

我们已知命名空间中可以定义变量,那么命名空间中的变量是局部变量还是全局变量呢?

这个问题我们得回到局部变量和全局变量的定义去看

  • 函数里面的局部变量是定义在栈帧上的,只有去调用函数才会去建立栈帧!
  • 而全局变量是建立在静态区!整个工程都可以使用!

那么命名空间中的变量是定义在哪里呢?

**在静态区中!**所以命名空间里面变量的本质就是一个全局变量!

一般的全局变量毫无遮掩,我们直接就能看到,==但是命名空间里面的变量相当于在四周被加了围墙,我们只能到墙里面才能看到它!(使用作用域限定符)==,这样子来理解可能会比较直观!

1.3命名空间的合并和嵌套

当我们遇到多个头文件使用命名空间时都命名为同一个名字时会发生什么?

答案是会发生合并!==或者是说同一级的同名命名空间会发生合并==因为当我们调用多个头文件时候,在预编译阶段就会将头文件里面的内容都拷贝到该源文件的位置!

初步认识c++之命名空间详解(千字长文带你刨析你命名空间的细节)

//上面的程序等同于
namespace test
{
	struct  ListNode
	{
		int val;
		struct ListNode* next;
	};
}
namespace test
{
	struct stack
	{
		int* a;
		int top;
		int capacity;
	};
}

int main()
{
	test::ListNode a;
	a.val = 0;
	a.next = NULL;

	test::stack b;
	b.capacity = 0;
	b.top = 0;
	b.a = NULL;
	printf("val:%d capacity:%d top:%d", a.val, b.capacity, b.top);
}

但是不同一级的命名空间就不会发生合并!

namespace test
{
	struct  ListNode
	{
		int val;
		struct ListNode* next;
	};
	namespace test
	{
		struct stack
		{
			int* a;
			int top;
			int capacity;
		};
	}
}
int main()
{
	test::ListNode a;
	a.val = 0;
	a.next = NULL;

	test::stack b;//test::test::stack b;这才是正确写法
	b.capacity = 0;
	b.top = 0;
	b.a = NULL;
	printf("val:%d capacity:%d top:%d", a.val, b.capacity, b.top);
}//这样写的话程序就会出现报错!

同样的这也是我们要说的命名空间的嵌套!==命名空间里面是可以嵌套命名空间的!==

1.4官方命名空间std

c++官方为了防止出现冲突,于是将c++标准库里面的东西都放进了std这个命名空间里面!

所以我们经常看到写c++代码的时候会出现以下两行代码

#include <iostream>
using namespace std;

这就是在使用std这个命名空间,将其全部展开!

1.4.3命名空间的使用方式

首先就是将其全部展开就是我们上面看到的那个方法

#include <iostream>
using namespace std;
int main()
{
   	cout << "hello world" << endl;
}

全部展开后我们就不用那么麻烦的在使用命名空间定义的变量名/函数名/类型前面加上 std::

==但是!这样其实是不好的!因为这样子舍弃了命名空间原本最大的用处!防止出现冲突!==所以我们应该尽量的防止进行全部展开!特别是在进行多人合作的时候!

如果我们又想既不那么麻烦的使用常用函数,又不舍弃命名空间的优点这该怎么办?

所以c++提供了另一种方式对命名空间进行半展开,将我们经常使用的函数进行展开,而不常用的函数就不进行展开!

#include <iostream>
using std::cout;
int main()
{
   	cout << "hello world" << std::endl;
}

当然了最后一种就是完全不进行展开

#include <iostream>
int main()
{
   	std::cout << "hello world" << std::endl;
}