头文件与cpp文件为什么要分开写

时间:2022-04-09 16:16:45

最表面的机制是:

头文件是程序的界面(是代码界面),提供给程序员以  类、模版、函数等一系列的声明,让程序员知道应该怎么调用里面的“东西”。

从动态链接库的角度看:

头文件提供界面,使得程序员在需要加载一个库函数的时候(这里也仅仅是举简单的例子)查看头文件就知道怎么加载这个动态库内部的函数。

从软件的扩展来说:

将头文件作为界面,再去定义它的实现,这样只要保证界面不变(头文件不变),就可以只修改实现文件,而不必修改其他的实现代码。比如你有一个sort()函数来排序,在一个大程序中,你后来发现这个sort()有更好的算法,于是你只需要去修改函数的实现(修改.cpp文件的sort()函数的代码),其他使用这个函数的地方可以完全保持不变,这是分割技术的第一个好处。

从模块性来讲:

界面后面隐藏实现代码,代码具有更好的物理模块性,减小程序的复杂度。

从编译的角度看:
所有源文件都是被编译器分别划分单元来分别编译,在编译的过程中,头文件被嵌入到实现文件里面一起作为一个编译单元被编译(实现文件 filename.cpp 里的 #include "filename.h" 那一行被替换成 filename.h 里面的所有内容(实际上会把预处理指令去掉,这才是预处理最本质的作用))。
举一个简单的例子,你定义了sort()函数,在test.h头文件里声明,在test.cpp里定义,这个时候在test.cpp里面#include "test.h",并定义sort()函数。
你需要在头文件内部写预处理代码
#ifndef _TEST_H_
#define _TEST_H_
/*中间是该头文件的一系列声明*/
#endif

预处理指令防止头文件被多重包含,如果你的代码出现了诡异的错误,请注意这个问题,可能是你没有写头文件保护,在符号链接阶段链接器发现有多个相同的名字,它不知道应该跟哪一个符号链接在一起,所以报错。

从节约时间的角度看:
在一些大型项目里面,编译一个项目不是整个一起同时编译的,一般情况是分别交给几个负责人去编译,测试,最后链接起来。如果中间发现有一个实现文件有BUG,只需要修改这个.cpp文件,其他的文件就可以原封不动。再重新编译的时候,只需要单独编译修改过的这一块,其他的部分不动。然后再链接成可执行程序。
这种机制在一个可能完整编译需要花费超级计算机几天时间的项目上是非常有用的,想一下,没有这种机制,要是一个程序随便修改一个地方。这个程序就要花几天时间来编译,这事情,怎么了得?
使用连接器的两个原因:
1、.编译成目标文件的时候代码的内部符号被编译器修改过。
2、目标文件被组织成一个整的大型文件,所有符号被定位,保证每一个函数调用都找得到他本身的定义位置

比如现在一个项目中有N个文件,其它n-1个都要用到第n个文件中的某个函数func(), 那么此时我们把第n个文件编写成n.h, n.cpp, 在n.h声明这个函数func(),然后在n.cpp中实现这个函数, 那么在其它n-1个要用到这个函数时,只需要在开始处加一句 #include "n.h" 即可。

我们可能会自定义很多函数 而这些函数分别会在不同的地方被调用 甚至有些时候我们需要把一堆函数打包起来一起调用比如#include "stdio.h" 的意思就是告诉系统我要用输入输出函数包(确切的讲,stdio.h就像是一个程序包一样,里面打包了各种各样的输入输出函数,stdio里的io就是in out的意思,很形象吧)

说说具体的吧 为了方便函数可以独立的被任何地方调用 我们会把每一个函数的定义代码都写成一个cpp 这个叫做函数的代码文件并且此cpp的文件名以此函数名来命名

同时 我们会建立一个文件名相同的头文件(即.h)并在该头文件里写上此函数的函数声明(如 void a();等等)别看头文件里只写了简单的一句 但却很有用

如果某个函数里需要调用我们刚才定义的那个函数的时候之需要在代码里加上一句#include "xxxx.h"就可以了(xxxx就是刚才我们自己建立的那个头文件)

这样说好像还是体现不出这种发的优势 那么看看头文件的另一种用法

假如我们现在要开发一个视频编辑软件 那么里面必然要定义很多很多函数 有关于图像处理的 关于音频处理的 等等等等在没有头文件的情况下 如果我们的main()函数里需要调用关于图像处理的很多函数那么我们就得在调用语句的前面一个一个的对所用到的函数进行声明(否则系统就会报错 说你的函数没有定义) 而用了头文件就方便多了我们只需要建立一个tuxiang.h 并在里面写上所有的关于图像处理的函数的声明(相当于我们建立了一个图像函数包) 这样当任何一个函数想要调用关于图像处理的函数时 无需任何声明 只需在代码前加上一句#include "tuxiang.h"就可以了这样就避免了每一个cpp里调用图像函数包都得写一大堆声明。

但是有时候并不是要把所有的函数和变量声明都放到头文件,之所以放到头文件中是方便在其它地方调用,但如果我们不希望被别的函数在引用本头文件时看到它的另外一些功能,这时就应该把它声明到.cpp文件中。比如有 func1.h ,func1.cpp ,func2.cpp, 其中func1.cpp,func2.cpp都需要调用func1.h中声明的某个函数,那么自然就要#include "func1.h", 但是在我们希望func2不能看到func1中的show_password()函数,此时怎么办? 把show_password()这个函数包括声明,定义及实现都放到func1.cpp中,这样即便func2.cpp 包含了func1.h 它也看不到show_password()这个函数的存在, 看不到自然也无法调用此函数。