#include与using的区别

这是一个C++里的老问题了,就像sizeof与strlen一样,困扰着很多初级程序员,所以估计看这篇文章的读者大都也是刚入门C++不久的新手,所以就不谈那些大道理了,而且那些文章已经不少了。

本文将从一个个程序试验入手,让大家对问题有个比较充分感性认识。也希望大家不要手懒,有条件还是跟着一步一步做的好。毕竟学程序,敲代码的过程谁也替不了你。

让我们先看一个简单的:(命名为main.cpp)

<span style="font-size:14px;">/*
* main.cpp
* 一个简单的小程序
*/
int main()
{
	int a = 1;
	return 0;
}</span>
这个程序什么也没做,只是定义了一个变量,然后就结束了。这东西当然没有实用意义,我们得用它做点儿什么才行,哪怕只是输出一句话呢!不过先不要着急,我们先来定义一个头文件和对应的源文件,为简便起见,让我们给它们起名叫1.h和1.cpp,内容如下:

<span style="font-size:14px;">/*
* 1.h
* 含有一个函数声明
*/
#ifndef _1_H	/*头文件包含*/
#define _1_H

void fun();	//声明一个函数

#endif</span>
<span style="font-size:14px;">/*
* 1.cpp
* 定义了一个函数
*/


void fun(){
	int a;
}</span>
然后将1.h包含到main.cpp中:

<span style="font-size:14px;">/*
* main.cpp
* 包含一个头文件
*/
#include "1.h"
int main()
{
	int a = 1;
	fun();
	return 0;
}</span>
常识着运行一下,没有错误。然后将main.cpp中的#include "1.h"注释掉,再运行一下,就不行了,提示fun()是未声明的标识符。这说明#include "1.h"的作用是将fun()的声明弄到了main.cpp中,实际上编译器做的事情就是将#include "1.h"这句话换成了1.h文件中的内容。完全是物理意义上的替换。

现在让我们回到输出的问题上,也就是cout了。但你发现直接写cout是会报错的,同样是未声明的标识符。这是因为cout和我们的fun()一样,是外来物种,并不是像int,return一样是C++语言的组成部分。所以必须要包含其头文件才可以使用,就像包含1.h一样。这也就是#include <iostream>的由来,让我们写上这句话,同时为了程序顺利运行,先把using namespace std语句也写上,一会儿再做解释。

<span style="font-size:14px;">/*
* main.cpp
* 加入<iostream>后
*/
#include <iostream>
#include "1.h"

using namespace std;

int main()
{
	int a = 1;
	fun();
	cout<<a<<endl;
	return 0;
}</span>
这样一运行就会把a输出了。到此为止,事情好像完美解决了,我们可以通过文件包含来组织我们的项目,还可以使用系统给我们提供的各种工具,比如用cout将信息输出至屏幕。

但实际开发中我们的项目文件是非常多的,它们可能是由不同的人编写出来的,并且相互之间形成复杂的包含层次。这时候就出现了同名问题,下面的代码又定义了一个头文件和对应的源文件:2.h和2.cpp。并且在里面定义了与1.h同名的函数fun():

<span style="font-size:14px;">/*
* 2.h
* 含有一个函数声明
*/
#ifndef _2_H	/*头文件包含*/
#define _2_H

void fun();	//声明一个与1.h中函数同名的函数

#endif</span>

<span style="font-size:14px;">/*
* 2.cpp
* 定义了一个和1.cpp中同名的函数
*/

void fun(){
	int a;
}</span>

现在让我们把2.h也包含到main.cpp中

<span style="font-size:14px;">/*
* main.cpp
* 加入2.h后
*/
#include <iostream>
#include "1.h"
#include "2.h"


using namespace std;


int main()
{
	int a = 1;
	//fun();
	cout<<a<<endl;
	return 0;
}
</span>

这时候我们根本不用调用fun()编译也不会通过(其实是链接错误,一会儿再细说,反正是不能运行),甚至将前面的#include "1.h"和#include "2.h"删去也不行!这说明我们所有cpp文件中的所有函数名和变量名都不可以重复,否则就会被编译器判定为重复定义。那该怎么办呢,把其中一个的名字改了?这当然也是一种办法,但就像刚才提到的,我们的项目可能有成百上千个文件,而且还不是一个人写的,要保证里面没有同名变量实在有点儿困难。

让我们回到问题本身,编译之所以无法通过的原因就是编译器把所有的cpp文件中的名字“一锅烩”了,里面有重复的就报错。那能否不让编译器把他们放到一起呢,而是这部分放一起,那部分放一起呢?这个时候就要说到命名空间了,命名空间,顾名思义就是给命名分空间的,这个命名空间里的名字是“一锅”,那个命名空间里的名字是另“一锅”。不同锅里的名字可以重复,这样问题不就解决了吗。让我们把代码修改一下:

<span style="font-size:14px;"><span style="font-size:18px;">/*
* 1.h
* 加入了命名空间的定义
*/
namespace ns1{
	#ifndef _1_H	/*头文件包含*/
	#define _1_H

	void fun();	//声明一个函数

	#endif
}</span></span>

<span style="font-size:14px;"><span style="font-size:18px;">/*
* 1.cpp
* 加入了命名空间的定义
*/
namespace ns1{
	void fun(){
		int a;
	}
}</span></span>

<span style="font-size:14px;"><span style="font-size:18px;">/*
* 2.h
* 加入了命名空间的定义
*/
namespace ns2{
	#ifndef _2_H	/*头文件包含*/
	#define _2_H


	void fun();	//声明一个与1.h函数同名的函数


	#endif
}</span></span>

<span style="font-size:14px;"><span style="font-size:18px;">/*
* 2.cpp
* 加入了命名空间的定义
*/
namespace ns2{
	void fun(){
		int a;
	}
}</span></span>
先不要修改main.cpp,这个时候程序可以运行了。这说明我们定义的ns1和ns2像是两口新锅,将两个锅里定义的名字分开了,编译器将它们分别处理了。这是个好消息,但当我们去掉main.cpp中的fun()的注释后发现新问题来了,编译器提示未定义标识符。

这里要打断一下,这时候#include的作用并没有消失,编译器还是把两个头文件里的东西复制到了main.cpp里,只不过他们都嵌套在namespace里了,即便是在一个文件里,也无法访问到里面的内容。

这说明不同锅里的变量是不能随便相互引用的,我们得想办法把一个锅里的东西倒进另一个锅里才行。注意这个时候其实是有三个锅,一个ns1,一个ns2,还有一个是我们主函数所在锅,就叫它主锅吧(其实是默认命名空间)。

我们现在要在主锅里引用ns1或者ns2里的东西,这里注意只能引用其中一个,否则就又回到原点了,两个一样的名字在一个锅里是不可以的。问题搞明白了,方法估计你已经想到了,那就是用using namespace ns1/ns2(注意只能选其中一个!)将ns1或者ns2锅里的东西倒过来就可以了。这个代码就不给出来了,自己加上试一下就好。

解决了这个问题那std的问题就迎刃而解了,std就是一个已经定义了很多名称的命名空间。我们要用里面的东西就必须把它倒进我们的锅里。

还有一个萦绕在很多程序员心中的问题是到底谁包含谁的问题。是ns1包含“1.h”还是相反呢?其实这还是没有真正理解二者作用的表现,既然说到了,就多说几句,#include的作用是帮助编译(这个编译是狭义上的编译,不包含链接)过程的,包含进来的各种声明是告诉编译器这个名字是合法的,可以在文件中出现的,这样编译器就将各个cpp文件编译成了多个目标模块。到此为止,两个fun()还相安无事的呆在自己的模块里。下面就是链接了,链接的过程是为每个名字找到对应的定义,也就是对应的内存地址,这个时候就出问题了,在我们的项目中出现了两个fun()的定义,那使用时该用哪个呢?这显然是个问题,所以就出现了一个连接错误,说fun()重复定义了,不知道用哪个了。而namespace就是这个时候出现的,它实现了一种为名字找定义的机制(using声明并不会复制什么代码到你的文件里!),即链接时只在同一个锅里找定义,其他锅里有没有不管。总而言之,#include管编译,namespace管链接,二者谁也替代不了谁!

就像sizeof和strlen一样,#include和using也一样,看似关系紧密,实则貌合神离。二者可以说是没有一毛钱的关系。每个东西有每个东西自己的作用,就是这么简单。

最后,本人也是正在通往C++的路上,能力还相当有限,文中有误人子弟的地方还请各位批评指正。

  • 13
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值