admin 管理员组

文章数量: 887021

【C++】

本篇文章我将向大家介绍C++中一个非常重要的语法,命名空间的概念。

首先一开始我想先请大家一段C语言代码:

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

很简单的一段代码,这里我问一个最简单的问题,我的这段代码有没有错?

这个时候可能有人觉得,必然是错的,scanf是C语言的一个标准输出函数啊,你怎么能用函数名来当变量呢?

我要告诉大家的是,C语言中共有32个关键字,而C语言语法中只是规定不能用关键字来命名变量,但是并没有规定不能用函数名来命名。也就是说我用scanf来做变量名从语法角度出发是没有任何问题的,不过我想说的是,这段代码确实是有问题的,下面我们就来编译一下这段代码:

可以看到,提示的错误信息是scanf被重定义了,下面我来解释原因。

我想大家C语言阶段都曾学到过,生成一个可执行程序需要预处理,编译,汇编,链接四个过程。而预处理阶段会将头文件里包含的所有函数都展开,而scanf是<stdio.h>这个头文件里包含的一个函数,也就意味着这个函数在预处理之后会被展开到当前代码中。

这样当你在main函数中打印scanf的时候就会出现问题了,你要打印的scanf究竟是库函数中的函数名,还是你主动创建的全局变量,这就会产生歧义,从而导致错误。

这就是C语言语法中一些不严谨的地方。这里可能有人要说了,这有什么,那我定义变量名的时候小心一点,尽量不和库函数名发生冲突不就好了。

这里我来为大家举一个例子,假设你现在是一名公司的员工,你们组一共是十个人,现在正在共同完成一个项目。实现一个大项目,肯定是需要协作进行的,你的组长现在把这个项目分为好几个模块,然后你们每个人负责一部分。当你和你的组员都完成之后,再把这些模块汇总成完整的项目。

现在你有一个同事张三,你在你所负责模块的时候创建了一个变量length,而张三又在它的模块里创建了一个同样命名为length的变量。你们两个创建的变量作用肯定是不同的,但是变量名却一样,这样最后在汇总的时候项目中是不是就会出现两个名为length的变量,从而导致变量的重定义。

我举这个例子的目的是为了告诉大家,有时候用C语言写代码的时候是难以解决命名冲突的问题的,而C++中为了解决这个问题就提出了命名空间的概念。

下面我们就来看命名空间是一个怎样的东西。


目录指引

  • 1. 命名空间的概念
  • 2. 命名空间的定义
  • 3. 命名空间的使用
    • 3.1 加命名空间名称及作用域限定符
    • 3.2 使用using将命名空间中成员引入
    • 3.3 使用using namespace命名空间名称引入
  • 4. 命名空间补充知识
    • 4.1 命名空间里的内容既可以定义变量,也可以定义函数
    • 4.2 命名空间可以嵌套
    • 4.3 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。

1. 命名空间的概念

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

2. 命名空间的定义

定义命名空间,需要使用到namespace 关键字,这个关键字的作用就是定义一个命名空间。namespace后面跟 命名空间的名字 ,这个名字就跟变量名一样可以随便取,然后 接一对{} 即可,{}中即为命名空间的成员。

现在我们就来对上面的错误代码进行修正:

#include <stdio.h>namespace ret 
{int scanf = 0;
}int main()
{	printf("%d\n", scanf);
}

上述代码中我将定义的名scanf的全局变量封装在了名为ret的命名空间中了,命名空间就相当于C++中一块被隔离起来的空间,只要你不将它打开,里面的东西就不会出来。下面我们再来运行一下这段代码:

可以看到这次并未发生命名冲突,成功的打印出了一个值到屏幕上。这是因为命名空间的存在将我们原本定义的全局变量scanf给封装了起来,只要不打开,外界就不会接收到这个变量。因此代码中只有标准输入函数scanf,最终打印的结果是将该函数的地址以十进制的形式打印到了屏幕上。

好,到这里我们算是初步的认识了一下命名空间是如何定义的,以及它的作用。接下来的问题是,你把变量封装在命名空间中,总得取出来用吧,关起来不用,那你还定义它干嘛。

下面来看看我们是如何使用命名空间的。

3. 命名空间的使用

下面我来介绍命名空间的三种使用方法。

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

#include <stdio.h>namespace N
{int a = 10;int b = 20;int c = 30;
}int main()
{printf("%d\n", N::a);return 0;
}

代码中的 '::'为域作用限定符 ,N::a的作用是直接指定a变量就是N空间里命名的变量。

运行结果:

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

#include <stdio.h>namespace N
{int a = 10;int b = 20;int c = 30;
}using N::b;int main()
{printf("a = %d\n", N::a);printf("b = %d\n", b);return 0;
}

using也是c++中的一个关键字,该关键字的作用是展开命名空间。代码中的using N::b指的是把b单独展开在全局域中,展开之后的b就相当于一个没有被封装的全局变量,可以在任何地方调用。

运行结果:

3.3 使用using namespace命名空间名称引入

#include <stdio.h>namespace N
{int a = 10;int b = 20;int c = 30;
}using N::b;
using namespace N;int main()
{printf("a = %d\n", N::a);printf("a = %d\n", b);printf("c = %d\n", c);return 0;
}

using namespace N相当于将命名空间N里的变量全部展开到全局域中,这样域中所有变量的调用都会不受限制。

运行结果:

注意:第三种方法并不推荐使用,因为全部展开根本没有起到命名空间的效果。不过第三种方法在展开比较方便,日常练习中可以使用,但如果是在大型项目中还是推荐使用前两种方法。

4. 命名空间补充知识

4.1 命名空间里的内容既可以定义变量,也可以定义函数

//1. 普通的命名空间
namespace N1 // N1为命名空间的名称
{// 命名空间中的内容,既可以定义变量,也可以定义函数int a;int Add(int left, int right){return left + right;}
}

4.2 命名空间可以嵌套

//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()
{printf("%d\n", N2::a);printf("%d\n", N2::Add(3, 4));printf("%d\n", N2::N3::c);
}

可以看到嵌套命名空间在展开的时候利用域作用限定符逐层展开即可。

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

//3. 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。//a.cpp
namespace N1 // N1为命名空间的名称
{// 命名空间中的内容,既可以定义变量,也可以定义函数int a;int Add(int left, int right){return left + right;}
}//b.cpp
namespace N1
{int Mul(int left, int right){return left * right;}
}

命名空间的介绍到这里就全部结束了,学完命名空间之后大家一定也感受到它的优势了。再拿我最开始的例子来举例,当你和张三在命名变量的时候,你的变量就放在你的命名空间,张三的变量放在他的命名空间中。在项目汇总时,如果要用你的变量就从你的命名空间中取,用它的就从他的命名空间中取,这样就永远不会发生命名冲突了。

最后希望这篇文章能够为大家带来帮助。

本文标签: c