本文涉及:
C++的编译逻辑解释
头文件的详细解释
从完整的程序中分离出一个头文件,并将其包括进源文件内
如何包含头文件 头文件与源文件存放位置的解释
C++预处理器及其命令的解释 #inlcude指令的解释 利用#if和#endif指令注释代码
利用预处理器指令解决程序模块化后不同的文件中重复声明同一个类的问题
命名空间的详细解释
头文件中能包含的参数 头文件中不能包含的参数的总结
C++的编译模式与程序模块化的关系
通常,在一个C++程序中,只包含两类文件.cpp
文件和.h
文件.其中.cpp
被称作C++源文件,里面放的都是C++的源代码.而.h
文件被称作C++头文件,里面放的也是C++的源代码.
由于C++支持分别编译 ,我们可以把程序划分成多个组成部分(即所谓的"模块化"),这是通过把程序代码分散到多个文件里,等编译程序的时候再把这些文件重新组在一起而实现的.
模块化(modularization)
C++支持 分别编译 ,也就是: 一个程序的内容,可以分成不同的部分放在不同的.cpp文件中,而.cpp中的东西都是相对独立的,在程序的编译过程(compile)时不需要和其他文件互通,只需要在编译成目标文件后再与其他的目标文件做一次来链接(link)就好了.比如:
此时我们在
a.cpp
这个源文件中定义了一个全局函数"void a(){}"
,而在文件b.cpp
中需要调用这个函数,即使这样,文件a.cpp
和b.cpp
并不需要互相知道对方的存在,而是可以分别的对它们进行编译,编译成目标文件时再将这个两个源文件互相链接,程序就可以运行了.
这是如何实现的: 在文件b.cpp
中,在调用"void a()"
函数之前,先在b.cpp中
声明一下这个函数"void a()"
就可以了,这是因为编译器在编译b.cpp
的时候生成一个符号表(symbol table),像"void a()"
这样的看不到定义的函数符号,通过b.cpp
文件中的提前声明,就会被存放到这个表中, 有了这个表,程序在进行各源文件链接的时候,编译器就会去别的目标源文件中寻找这个"void a()"
的定义,程序找到某源文件的定义后,程序便可以顺利运行了.
声明:而"声明"则只是声明这个符号的存在,即告诉编译器,这个符号是在其他文件中定义的,我这里先用着,你链接的时候再到别的地方去找找看它到底是什么吧,声明在不同的文件中可以有多个声明.
定义:定义就是把一个符号完完整整地描述出来,它是变量还是函数,返回什么类型,需要什么参数等等,定义一般需要在某个文件中存在一个定义(重载和覆盖除外).
一个符号,在整个程序中可以被声明多次,但却要且仅要被定义一次,这种机制引出了一种编写程序的方法: 如果有一个名为"void fun()"
在整个程序中许多.cpp
文件中都会被调用,那么我们就需要在某一个文件中定义函数,而在其他的文件中声明这个函数就可以了,但是这种方法也引发出了一个问题: 如果在某个文件中声明一个函数还好,但是如果函数多了,有好几百个,这怎么办?能保证每个程序员都可以完完全全把所有函数形式都准确记下来并写出来吗?所以,此时我们需要一个头文件,专门用来存放这个函数的声明.
头文件
什么是头文件,为什么需要头文件
所谓的头文件,其实它的内容和.cpp文件中的内容是一样的,都是C++的源代码,但头文件不用被编译,我们把所有的函数声明全部放进一个头文件中,当某一个.cpp
文件需要某个函数声明时,它们就可以通过一个宏命令"include"
把包含了这个函数声明的头文件包含进这个.cpp
文件中,从而把它们的内容合并到.cpp
文件中.当.cpp
文件被编译时,那些被包含进去的.h
头文件的作用便发挥作用了.
如果只用一个源文件来保存程序的全部代码,虽然这是可行的,但是这会给程序的编辑和修改工作带来诸多的不便(容易因为程序某处的小问题而牵一发动全身).
为了规避这个问题,我们可以借助C++的预编译器和编译器的能力把一个复杂的应用程序划分成多个不同的文件,同时仍能保持它在内容和程序功能上的完整合理.
#include 指令
C++预处理器的 #include
指令,提供了一种能够让编译器在编译主程序的时候把其他文件的内容包括进来的机制.(例如用这个指令来包括 iostream
这样的头文件)
include
是一个来自 C 语言的宏命令,它在编译器进行编译之前,即在预编译的时候就会起作用。
#include 的作用是把它后面所写的那个文件的内容,完完整整地,一字不改地包含到当前的文件中来。值得一提的是,它本身是没有其它任何作用与副功能的,它的作用就是把每一个它出现的地方,替换成它后面所写的那个文件的内容.简单的文本替换,别无其他.
之所以要包含头文件,是因为头文件提供了必要的函数声明和类声明(比如string头文件就定义了字符串应该如何被创建和使用)
头文件是一些以.h
为扩展名的标准文本文件。一般情况下,都应该 把自定义的头文件和其余的程序文件都放在同一个子目录里,或者在主程序目录下专门创建一个子文件夹集中存放这些头文件.
与标准的C++源代码文件[.cpp]相比,在头文件里应该使用更多的注释
这是因为绝大多数头文件是通用型的,不隶属与任何特定的程序,所以至少应把它的用途和用法描述清楚.
应该在注释里说明的内容包括: 创建头文件的日期,文件用途,创建者姓名,最后一次修改的日期,有什么限制和前提条件等…其次头文件中的每一个类和函数都应该有相应的说明.
头文件可以细分为系统头文件和自定义头文件
系统头文件(C++标准STL类库的头文件):
例如:
#include <iostream>
为了和以前C语言的标准头文件作区别,C++中完成了标准化的头文件是没有.h
结尾的,其称为C++标准头文件,其定义的都是系统级的功能,正是因为有了它们,C++代码才可以在某种特定的系统上运行(可移植性),如果想在程序中使用这些功能,就必须把相应的头文件包括到程序中来.
系统头文件的另一个重要作用是保证C++代码的可移植性,确保同样的C++代码在不同的操作系统上做同样的事情(比如iostream中的cout指令,在windows和linux系统中内部底层实现肯定是不一样的,但是由于头文件的封装,我们不需要改变cout指令就能在这两个不同的系统中同样实现输出的功能)
在 #include
指令里面,系统头文件的文件名需要放到尖括