1. c/c++ 头文件包含
1.1 c++ 程序的 编译链接过程
包含的头文件分为三类:
- c++ 标准库头文件
- c++ 第三方库 头文件
- 自定义的类等 头文件
1.2疑问
为什么系统静态库、和第三方静态库动态库 等不提供源码直接给我们链接呢?而是已经编译好的静态库或者动态库 .lib 或者.so 文件呢?
- 原因一:知识产权的原因,有的第三方库不愿意提供源码。
- 原因二:编译效率原因,直接使用编译好的就不需要我们进行编译了。
1.3 动态库与静态库的区别
静态库在链接的时候和其他代码一起生成一个可执行文件,而动态库只是记录了动态库的加载位置。真正等到可执行文件执行的时候,才回去调用需要调用的静态库。这样的好处是:如果多个软件依赖某些共同的静态库,那么,实际上在内存中只有一份代码,也就是起到了节约内存的目的。还有一个优点就是动态库升级比较方便。 但是缺点就是,可执行文件在执行时,必须能够找到动态库。
2. 文件包含的实质
包含文件: 实际上就是文件的拼接
情况一:主程序包含B,B又包含A。 等价于A、B、主程序三者的拼接。
编译单位 包含B.h。B.h又包含A.h,那么实际上这等于是 文件的拼接,最后的文件是A.h B.h 可编译单位.cpp 并且,包含的顺序就是如图所示的顺序。
3. 解决重复包含
既然 文件包含等价于文件的拼接,那么假如主函数依赖于(包含)A.h、B.h,而B.h又依赖于(包含)A.h。 这样的话,A.h 不就被包含了两次吗? 因此我们需要解决这个问题。
解决文件重复包含的办法就是:引入防伪式的声明。
#ifndef __A_H__
#define __A_H__
#endif
这样就解决了一个头文件被包含两次的问题。
4. 组织代码
假如把一个定义的类X的 声明和定义放在一个.h 文件中:
- 假如A文件需要引入这个类X,那么就需要包含这个头文件, 包含进去以后,就把这个文件 和 头文件中定义的实现 编译在一起生成一个 .o
文件。 - 假如B文件也需要包含这个类X,那么B文件就需要也包含这个头文件,编译后就把这个头文件中的实现 和 B文件一起生成了一个.o 文件。
- 如果C文件需要用到上面生成的.o 文件 那么就会出问题。
如果将一个类的定义和声明分开成两个文件放,声明在.h 文件中,实现在.cpp 文件中:
- A文件需要用到这个类的话,会包含这个类的声明。
- B文件如果使用的话也只是包含这个类的声明。
- C如果需要使用AB的话,A、B 包含的类X不会冲突。