1 现象
在使用VC++应用向导生成的源码框架时,无论是MFC应用程序,还是Win32窗体程序,都会默认启用“预编译头”功能,自动生成stdafx.h和stdafx.cpp这两个文件。以后向项目中增加源文件时,也自动对该源文件启用“预编译头”功能。要求必须在.cpp文件开头加入 #include "stdafx.h" ,否则编译器就会报错:fatal error C1010:在查找预编译头时遇到意外的文件结尾。是否忘记了向源中添加“#include"stdafx.h"”?
2 预编译头功能的实现
参与“预编译头”功能的源文件分成三种:
(1)(指示头文件)预编译头指示文件,如常见的stdafx.h;
(2)(创建/Yc)预编译结果的来源文件,如常见的stdafx.cpp;
(3)(使用/Yu)使用“预编译头”的.cpp文件;该文件必须需要包含,#include "stdafx.h"。
需要注意的是,任何源文件都可能作为上面三种角色中的一种,文件的名字也不是固定为stdafx.h 以及 stdafx.cpp,而是可以随意修改的。但是实际使用时,往往采用默认设置就行了。
VC++在编译项目时:
(1)首先编译产生预编译头的源文件,编译结果就是 Projectname.pch 文件;
(2)然后编译其它.cpp文件,如果一个.cpp文件使用了预编译头功能,那么在编译它时,首先要寻找预编译头指示行。如果找不到就会报错;如果找到了,则并不会像对待其他头文件一样包含进来再编译,而是直接使用 Projectname.pch参与该cpp文件的编译;
3 启用与禁用“预编译头功能”
“预编译头”作为VC++编译器的一项特性,这个项目范围内可以启用,也可以禁用;也可以每个源文件单独启用或禁用。
3.1 在项目级别启用与禁用“预编译头”
如果使用VC++的应用程序向导生成Win32或者MFC项目框架,那么该项目就会默认启用"预编译头"功能。如果想在项目建立后禁用这个功能,需要在项目属性里设置,如下图所示:
在这里选择“不使用”就可以禁用预编译头功能了。一旦在项目级别禁用,则所有的原来“使用预编译头”的源文件都会不再使用预编译头了。
3.2 在单个源文件级别启用与禁用“预编译头”
有时候建立的项目启用了”预编译头功能”,但是后来导入了其他的第三方源代码,那么这些源代码文件在导入时会自动启用“预编译头”功能,而这些源文件却没有包含 #include "stdafx.h" 这一行,所以编译时就会报错。此时解决方法有两个,一是为每个源文件增加这个指示行;二是对该源文件禁用”预编译头“功能。要想单独禁用一个cpp文件的”预编译头“功能,只要在VS中右键单击它,然后打开“属性对话框”,在其中修改即可。如下图所示:
4 “预编译头”的由来
在编译时,头文件不会直接参与编译,而是在预处理时与cpp文件合并后再参与编译。有些头文件几乎是所有cpp文件都需要包含的,如<stdio.h>,<windows.h>等,这样这些文件就需要在每个包含它的cpp文件中重复编译。早些时候,这些头文件不大,重复编译也浪费不了多少时间。然而到了MFC出现的时候,其头文件变得超级大,如就一个<afxwin.h>就长达6000多行,这样重复编译这些长的头文件就会造成更长的编译时间。为了解决这个问题,VC++编译器把这些公共头文件事先单独编译,然后在编译包含它的cpp文件时,就直接使用头文件编译结果参与cpp文件的后续编译任务。这种“一次编译,多次使用”的方法,确实能减少编译时间。
5 我的选择
单纯使用VC++开发项目,使用“预编译头”功能,有百利而无一害。然而有时候,我们的源文件需要实现跨平台,而VC++之外的编译器并不支持这种功能,此时就需要这些源文件单独禁用这个功能。把第三方源代码集成到VC++项目时,也需要禁用“预编译头”设置(VS支持同时选中多个源文件,同时启用/禁用预编译头),当然如果不嫌麻烦,也可以每个源文件开头都加上 #include "stdafx.h"这一行。