命名冲突问题与命名空间

文章介绍了C++中的命名空间概念,通过示例解释了如何避免命名冲突,包括定义命名空间、使用namespace关键字、域作用限定符以及using声明的三种方式。命名空间提供了一种组织代码和防止全局作用域污染的方法。
摘要由CSDN通过智能技术生成

一、何为命名空间?

首先我们运行下面代码,

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

我们会发现该代码能够正常运行,没有任何问题。

但是当我们再在上面代码的基础上包含stdlib.h头文件,代码还能正常运行吗?

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

这时我们会发现代码报错——“rand”重定义。这是因为在stdlib.h头文件中已经定义了rand()函数,与全局变量rand发生了命名冲突问题,从而导致printf()函数无法确定是输出rand全局变量的值还是输出rand()函数的地址。

在日常写代码的过程中,我们自己定义的变量、函数很有可能跟C++库发生命名冲突问题。并且进入公司项目组以后,做的项目通常比较大,多人协作也很有可能造成命名冲突问题。

但是C语言没有办法很好地解决这个问题,若非要有办法的话,也就只能是给重名的定义更换其他名字。

但这样做会导致工作效率十分低下,为了更高效地解决此问题,C++提出了一个新语法——命名空间

在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。

二、命名空间定义

定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。

如下面代码:

#include <stdio.h>
#include <stdlib.h>

namespace zjd  // zjd为命名空间的的名称
{
	int rand = 0;
}

int main()
{
// rand先到局部范围内找,若没有再到全局范围内找,若还没有则程序会报错,不会到定义的域里面去找
	printf("%d\n", rand);  // rand访问的是全局变量,rand()函数
	
// rand指定到定义的zjd域里面去找
    printf("%d\n", zjd::rand);  // ::域作用限定符

	return 0;
}

该代码定义了一个名为zjd的命名空间(域),与全局作用域进行了隔离,不会发生命名冲突问题。

Note:

①命名空间定义和结构体定义写法类似但又不同,结构体定义了一个新的数据类型,命名空间定义了一个新的域。

②域作用限定符(::)

当域作用限定符(::)前面指定域名时,会到指定的域中寻找变量、函数和类。

当域作用限定符(::)前面为空白时,会到全局域中寻找变量、函数和类。

当没有域作用限定符(::)前面指定域名时,会依次到局部域、全局域中寻找变量、函数和类。若局部域和全局域都没有找到,程序会报错。

int a = 0;
int main()
{
	int a = 1;
	printf("%d\n", a);  // 输出局部变量a —— 1
	printf("%d\n", ::a);  // 输出全局变量a —— 0
	return 0;
}

③一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中。

1、普通的命名空间

命名空间中的内容,既可以定义变量,也可以定义函数,还可以定义类型。

namespace N1
{
	int a;  // 变量

	int Add(int left, int right)  // 函数
	{
		return left + right;
	}

	struct Node  // 类型
	{
		struct Node* next;
		int val;
	};
}

int main()
{
	N1::a = 10;
	printf("%d\n", N1::a);

	int sum = N1::Add(1, 2);
	printf("%d\n", sum);

	struct N1::Node node;

	return 0;
}

2、命名空间可以嵌套

命名空间内部还可以嵌套定义命名空间。

namespace N2
{
	int a;
	int b;
	int Add(int left, int right)
	{
		return left + right;
	}

	namespace N3
	{
		int c;
		int d;
		int Sub(int left, int right)
		{
			return left - right;
		}
	}
}

int main()
{
	int sum = N2::Add(1, 2);
	printf("%d\n", sum);

	int dif = N2::N3::Sub(2, 1);
	printf("%d\n", dif);

	return 0;
}

3、多个命名空间名称相同

同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。

比如一个工程中的Test.h和Test.cpp中两个同名的命名空间会被合并成一个。

// Test.h
namespace N4
{
	typedef struct ListNode
	{
		struct ListNode* next;
		int val;
	}ListNode, * LinkList;

	void ListInit(LinkList ps);  // 声明
	void ListPushBack(LinkList ps, int x);  // 声明
}


// Test.cpp
#include"Test.h"
namespace N4
{
	void ListInit(LinkList ps)  // 定义
	{
		// 实现不展开写了
	}
	void ListPushBack(LinkList ps, int x)  // 定义
	{
		// 实现不展开写了
	}
}

int main()
{
	struct N4::ListNode node;
	N4::ListInit(&node);
	N4::ListPushBack(&node, 3);

	return 0;
}

三、命名空间使用

命名空间中成员该如何使用呢?比如下面代码是正确的代码吗?

namespace N
{
	int a = 10;
	int b = 20;
	int Add(int left, int right)
	{
		return left + right;
	}
	int Sub(int left, int right)
	{
		return left - right;
	}
}
int main()
{
	printf("%d\n", a);  // 该语句编译出错,无法识别a
	return 0;
}

由于局部域和全局域都没有a,并且未通过域作用限定符指定a的域,无法识别a,所以上面代码无法正常运行。

命名空间的使用有三种方式:

  • 加命名空间名称及作用域限定符
  • 使用using将命名空间中成员引入
  • 使用using namespace 命名空间名称引入

1、加命名空间名称及作用域限定符

在命名空间定义的讲解中,大家实际上已经掌握该方式的使用了。

namespace N
{
	int a = 10;
	int b = 20;
	int Add(int left, int right)
	{
		return left + right;
	}
	int Sub(int left, int right)
	{
		return left - right;
	}
}

int main()
{
	printf("%d\n", N::a);
	return 0;
}

Note:该方式能够做到最好的命名隔离,但是使用不方便,每次都需要指定域。

2、使用using将命名空间中成员引入

通过该方式我们可以将命名空间的某个成员展开,被展开的成员无需再通过域作用限定符指定域,但是未被展开的成员仍需要通过域作用限定符指定域。

namespace N
{
	int a = 10;
	int b = 20;
	int Add(int left, int right)
	{
		return left + right;
	}
	int Sub(int left, int right)
	{
		return left - right;
	}
}

using N::b;

int main()
{
	printf("%d\n", N::a);
	printf("%d\n", b);
	return 0;
}

Note:该方式可以用于展开常用的成员,也有较好的隔离效果。

3、使用using namespace 命名空间名称引入

通过该方式我们可以将整个命名空间展开,使用该命名空间里的变量、函数等无需再通过域作用限定符指定域了。

namespace N
{
	int a = 10;
	int b = 20;
	int Add(int left, int right)
	{
		return left + right;
	}
	int Sub(int left, int right)
	{
		return left - right;
	}
}

using namespace N;

int main()
{
	printf("%d\n", a);
	printf("%d\n", b);
	Add(10, 20);
	return 0;
}

Note:命名空间全部展开,用起来虽然极其方便,但是隔离彻底失效了。这种方式建议大家慎用。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

少校0778

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值