gta5玩法攻略:巧用头文件,多文件编译少发愁|浅谈实用“StdAfx” - 程序设计 - 如鹏网 计算机专...

来源:百度文库 编辑:偶看新闻 时间:2024/04/25 21:00:44

巧用头文件,多文件编译少发愁|浅谈实用“StdAfx”


        本文旨在分享我在编程过程中对于多文件编译的一点经验。文中谈到了因头文件重复包含而引起的重复定义问题及其解决方案,另外由此引伸出我对于StdAfx的相关认识。

       如果你同我一样,有颗强烈的好奇心;如果你同我一样,刚接触Visual C++ 6.0不久;如果你同我一样,对使用AppWizard(应用程序向导)创建程序模板时生成的“StdAfx.cpp”、“StdAfx.h”感到疑惑不解。那么我会很开心,不要误会,我是开心于下面这些文字也许对你有点帮助。如果你还没有接触Visual C++ 6.0,那也没关系,只要你在编程中使用过自定义头文件,有过多文件编译的经历,那么下面这些文字同样值得一阅。

一、缘于重复包含的重复定义错误

1、“重复包含”是个啥?

       “重复包含”顾名思义,即头文件在一个源文件或多个源文件中被多次包含,使得在编译时需要对该头文件中的代码进行重复编译,显然这是一种重复劳动,缺乏效率,而且还可能引起一系列问题。如果对我给出的解释感到晕乎,那么先来看几个重复包含引发错误的例子:

(1)C语言中的重复包含

例1.1.1(单源文件的重复包含):
创建一个源文件main.c,一个头文件head.h,如下:
  1.   
  2. //****************************   
  3. //*    头文件:head.h    *   
  4. //****************************   
  5.   
  6. int g_iNum = 1;  

  1.   
  2. //****************************   
  3. //*    源文件:main.c    *   
  4. //****************************   
  5. #include    
  6. #include    
  7. #include "head.h"   
  8. #include "head.h"   
  9. // 重复包含头文件head.h   
  10.   
  11. int main()   
  12. {   
  13.     printf(“%d\n”, g_iNum);   
  14.     system(“pause”);   
  15.     return 0;   
  16. }  

       对例1.1.1的进行编译,编译器报错,提示类似“redefined”(重复定义)这样的错误。这是因为头文件head.h在源文件main.c中重复包含两次,编译器处理时,头文件head.h在源文件main.c中被展开,由于是展开两次,因此语句“int g_iNum = 1;”出现两次,大致情况如下:
  1.     
  2. // 其他部分   
  3.   
  4. int g_iNum = 1;   
  5. int g_iNum = 1;   
  6. // 重复定义   
  7.   
  8. int main()   
  9. {   
  10.     printf(“%d\n”, g_iNum);   
  11.     system(“pause”);   
  12.     return 0;   
  13. }  

       显然这样是错误的,int型变量g_iNum在源文件main.c中连续定义两次。当然,这种错误一般人都不会犯,谁会故意将一个头文件在同一个源文件中包含两次呢?但是,如果将一个头文件包含到另一个头文件里,再将这两个头文件分别包含在一个源文件中,这样所产生的错误就比较常发生了,而且不容易察觉到,也许你就曾经犯过这样的错误。来看一下这种错误的例子。

例1.1.2(单源文件的头文件嵌套包含):
创建一个源文件main.c,两个头文件head1.h和head2.h,如下:
  1.     
  2. //******************************   
  3. //*    头文件:head1.h    *   
  4. //******************************   
  5.   
  6. int g_iNum1 = 1;  

  1.   
  2. //******************************   
  3. //*    头文件:head2.h    *   
  4. //******************************   
  5.   
  6. #include “head1.h”   
  7. // 嵌套包含头文件head1.h   
  8.   
  9. int g_iNum2 = 2;  

  1.   
  2. //****************************   
  3. //*    源文件:main.c    *   
  4. //****************************   
  5. #include    
  6. #include    
  7. #include "head1.h"   
  8. #include "head2.h"   
  9.   
  10. int main()   
  11. {   
  12.     printf(“%d\n”, g_iNum);   
  13.     system(“pause”);   
  14.     return 0;   
  15. }  

       在例1.1.2中,如果单独观察每个文件,不容易发现问题所在,需要假想编译器处理时的情况。首先头文件head1.h在源文件main.c中展开,于是源文件main.c中有拥有了语句“int g_iNum = 1;”。接着展开头文件head2.h,由于头文件head2.h中包含了头文件head1.h,故再次对头文件head1.h进行展开。此时,源文件main.c中将拥有两条“int g_iNum = 1;”语句,显然int型变量“g_iNum”被重复定义,编译器报错。

       一般的解决方案是将头包含命令“#include “head1.h””从头文件head2.h中删除。

(2)C++中的重复包含

       因为C++是兼容C语言的,所以上面的两个例子产生的问题在C++中同样会发生。而较之于单纯的C语言,C++中重复包问题更复杂。

       我们通常在定义类时,将类的声明和实现分开,声明部分放在头文件中,实现部分放在源文件中。这是一种好习惯,但是没处理好却也会让人大伤脑筋。来看下面的几个例子:

例1.1.3(单源文件的类声明头文件嵌套包含):
创建工程,定义一个点类CPoint,将声明和实现分别放在CPoint.h和CPoint.cpp中,定义一个线类CLine,将声明和实现分别放在CLine.h和CLine.cpp中,主函数在源文件main.cpp中:
  1.   
  2. //********************************************   
  3. //*    头文件:CPoint.h                     *   
  4. //*    描  述:点类CPoint的声明    *   
  5. //********************************************   
  6.   
  7. class CPoint   
  8. {   
  9.     protected:   
  10.         double m_dX;   
  11.         double m_dY;   
  12.     public:   
  13.         CPoint(double x = 0, double y = 0);   
  14.         virtual void set(double x, double y);   
  15.     double getX();   
  16.         double getY();   
  17. };  

  1.     
  2. //********************************************   
  3. //*    源文件:CPoint.cpp                  *   
  4. //*    描  述:点类CPoint的实现    *   
  5. //********************************************   
  6.   
  7. #include "CPoint.h"   
  8.   
  9. CPoint::CPoint(double x, double y):m_dX(x), m_dY(y)   
  10. {   
  11. }   
  12.   
  13. void CPoint::set(double x, double y)   
  14. {   
  15.     m_dX = x;   
  16.     m_dY = y;   
  17. }   
  18.   
  19. double CPoint::getX()   
  20. {   
  21.     return m_dX;   
  22. }   
  23.   
  24. double CPoint::getY()   
  25. {   
  26.     return m_dY;   
  27. }  

  1.   
  2. //*********************************************************************************   
  3. //*    头文件:CLine.h                                                                     *   
  4. //*    描  述:线类CLine的声明,CLine由CPoint的派生而来。    *   
  5. //*********************************************************************************   
  6.   
  7. #include "CPoint.h"   
  8. // 头文件嵌套包含   
  9.   
  10. class CLine:public CPoint   
  11. {   
  12.     protected:   
  13.         double m_dLength;   
  14.     public:   
  15.         CLine(double x = 0, double y = 0, double l = 0);   
  16.         void set(double x, double y, double l);   
  17.         double getLength();   
  18. };  

  1.   
  2. //*******************************************   
  3. //*    源文件:CLine.cpp                  *   
  4. //*    描  述:线类CLine的实现    *   
  5. //*******************************************   
  6.   
  7. #include "CLine.h"   
  8.   
  9. CLine::CLine(double x, double y, double l):CPoint(x, y)   
  10. {   
  11.     m_dLength = l;   
  12. }   
  13.   
  14. void CLine::set(double x, double y, double l)   
  15. {   
  16.     CPoint::set(x, y);   
  17.     m_dLength = l;   
  18. }   
  19.   
  20. double CLine::getLength()   
  21. {   
  22.     return m_dLength;   
  23. }  

  1.   
  2. //*******************************   
  3. //*    源文件:main.cpp    *   
  4. //*******************************   
  5.   
  6. #include    
  7. #include    
  8. #include "CPoint.h"   
  9. #include "CLine.h"   
  10.   
  11. using namespace std;   
  12.   
  13. int main()   
  14. {   
  15.     CLine line;   
  16.   
  17.     line.set(1.2, 2.3, 3.4);   
  18.     cout << line.getX() << endl;   
  19.     cout << line.getY() << endl;   
  20.     cout << line.getLength() << endl;   
  21.     system(“pause”);   
  22.     return 0;   
  23. }  

       对例1.1.3的工程进行编译,编译器报错,提示“CPoint”类重复定义。问题出在头文件CLine.h中,由于在其中嵌套包含了头文件CPoint.h,使得在编译源文件main.cpp时出错。首先,编译器遇到头包含命令“#include "CPoint.h"”,将头文件CPoint.h在源文件main.cpp中展开。接着,遇到头包含命令“#include "CLine.h"”,也将头文件CLine.h在源文件main.cpp中展开。由于头文件CLine.h中有头包含命令“#include "CPoint.h"”,于是再次将头文件CPoint.h源文件main.cpp中展开。此时,点类CPoint的声明部分在源文件main.cpp中出现了两次,重复了,于是报错。

       对于这种错误,一般有两种解决方案。一是将头文件CLine.h中的头包含命令“#include "CPoint.h"”删除,再在源文件CLine.cpp中的头包含命令“#include “CLine.h””之上添加头包含命令“#include "CPoint.h"”。二是直接将源文件main.cpp中的头包含命令“#include "CPoint.h"”删除。对于简单的几个文件而言,此方案足矣。然而对于一个包含很多文件的大工程工程,此法并非良策。在文章后面将会介绍到一种优越的解决方案。

       如果是将类的声明和实现都放在一个文件中会怎样呢?看下面例1.1.4:

例1.1.4(类的声明和实现不分开):
修改例1.1.3中的错误,并将类的声明和定义都放在同一个头文件中。
  1.     
  2. //*****************************************************   
  3. //*    头文件:CPoint.h                                *   
  4. //*    描  述:点类CPoint的声明和实现    *   
  5. //*****************************************************   
  6.   
  7. // 类的声明部分   
  8.   
  9. class CPoint   
  10. {   
  11.      protected:   
  12.         double m_dX;   
  13.         double m_dY;   
  14.     public:   
  15.         CPoint(double x = 0, double y = 0);   
  16.         virtual void set(double x, double y);   
  17.     double getX();   
  18.     double getY();   
  19. };   
  20.   
  21. // 类的实现部分   
  22.   
  23. CPoint::CPoint(double x, double y):m_dX(x), m_dY(y)   
  24. {   
  25. }   
  26.   
  27. void CPoint::set(double x, double y)   
  28. {   
  29.      m_dX = x;   
  30.      m_dY = y;   
  31. }   
  32.   
  33. double CPoint::getX()   
  34. {   
  35.      return m_dX;   
  36. }   
  37.   
  38. double CPoint::getY()   
  39. {   
  40.     return m_dY;   
  41. }  

  1.   
  2. //***************************************************************   
  3. //*        头文件:CLine.h                                                *   
  4. //*        描  述:线类CLine的声明和实现,CLine由CPoint的派生而来。*   
  5. //***************************************************************   
  6.   
  7. #include "CPoint.h"   
  8.   
  9. // 类的声明部分   
  10.   
  11. class CLine:public CPoint   
  12. {   
  13.     protected:   
  14.         double m_dLength;   
  15.     public:   
  16.         CLine(double x = 0, double y = 0, double l = 0);   
  17.         void set(double x, double y, double l);   
  18.         double getLength();   
  19. };   
  20.   
  21. // 类的实现部分   
  22.   
  23. CLine::CLine(double x, double y, double l):CPoint(x, y)   
  24. {   
  25.     m_dLength = l;   
  26. }   
  27.   
  28. void CLine::set(double x, double y, double l)   
  29. {   
  30.     CPoint::set(x, y);   
  31.     m_dLength = l;   
  32. }   
  33.   
  34. double CLine::getLength()   
  35. {   
  36.     return m_dLength;   
  37. }  

  1.   
  2. //*******************************   
  3. //*        源文件:main.cpp        *   
  4. //*******************************   
  5.   
  6. #include    
  7. #include    
  8. // 不再包含头文件CPoint.h   
  9. #include "CLine.h"   
  10.   
  11. using namespace std;   
  12.   
  13. int main()   
  14. {   
  15.     CLine line;   
  16.   
  17.     line.set(1.2, 2.3, 3.4);   
  18.     cout << line.getX() << endl;   
  19.     cout << line.getY() << endl;   
  20.     cout << line.getLength() << endl;   
  21.     system(“pause”);   
  22.     return 0;   
  23. }  


       将例1.1.4同例1.1.3比较,似乎例1.1.4更加简洁方便,可以少写些头包含命令。既然如此,那为什么平常我们会被建议将类的声明和实现分开来呢?这并不是空穴来风、混淆视听。当工程中含有多个源文件,而有两个或两个以上的源文件中包含了同一个类的头文件(包括声明和实现),那么问题就会凸显出来。为了演示这个问题,我们在例1.1.4的基础上,再加入一个源文件test.cpp和对应的头文件test.h,并在其中引用“CLine”这个类。
  1.   
  2. //*******************************   
  3. //*        源文件:test.cpp        *   
  4. //*******************************   
  5.   
  6. #include    
  7. #include "CLine.h"   
  8.   
  9. // 在源文件main.c中也包含头文件CLine.h   
  10.   
  11. using namespace std;   
  12.   
  13. void test()   
  14. {   
  15.     CLine line;   
  16.   
  17.     line.set(2.4, 1.5, 4.7);   
  18.     cout << line.getX() << endl;   
  19.     cout << line.getY() << endl;   
  20.     cout << line.getLength() << endl;   
  21. }  

  1.   
  2. //***************************************   
  3. //*        头文件:test.h                        *   
  4. //*        描  述:test.cpp中的函数声明        *   
  5. //***************************************   
  6.   
  7. void test();  

       如果对源文件test.cpp单独编译不会出错,而当构建整个工程时却出错了,错误提示大意是CPoint类和CLine类的成员函数已经在目标文件main.obj中定义了。为什么会这样呢?让我们追随编译器的脚步来看个究竟:编译器先对每个源文件单独编译,由于源文件main.cpp和源文件你test.cpp中都有头包含命令“#include “CLine.h””,对头文件CLine.h进行展开后,在这两个源文件文件中都存在点类CPoint和线类CLine的声明和实现代码,这一点并不影响各个文件单独编译。单独编译之后得到两个目标文件main.obj和test.obj。接下来将目标文件连接为一个文件,连接过程中发现在该文件中点类CPoint和线类CLine都被定义了两次,于是就报错。

       此时只得把按更正后的例1.1.3将点类CPoint和线类CLine的声明和实现分开来。因此,“将类的声明部分放在头文件中,而将实现部分放在源文件中”是很有必要的,特别是在多文件编程时。

       尽管如此,然而对于类模板却是另一番景象,类模板的声明和实现需放在同一个头文件中。从网络上的资料得知,如果要将类模板的声明部分和实现部分分开的话,需要使用关键字export。但是VC6等编译器都不支持关键字export,所以目前只能将二者放在一个头文件中。

2、解决重复包含的优化方案

       当一个工程的文件不多时,上述几个重复定义的解决方案并不会表现出明显的缺陷。而随着工程中文件的不断增加,这些解决方案便越发显得笨拙而费时,甚至让人无从下手。有没有更好的解决方案呢?当然是有的,否则就不会有这篇文章了!

       接触过VC的朋友可能会发现,在使用AppWizard(应用程序向导)创建程序模板时会自动生成源文件StdAfx.cpp和头文件StdAfx.h。如果你的好奇心够强烈的话,也许你会百度一下,了解有关StdAfx的相关资料。在此期间,或许你对阅读这些资料时感到吃力,不知所云。既然这样,那就先撇开那堆晦涩难懂的文字,来了解一下StdAfx对我们解决重复包含问题的巨大贡献吧。下面不讲理论,只浅显地模仿StdAfx的模式来解决重复包含问题。若是理论,我也谈不来。

       对于工程,我们大可不必借助程序模板,自己动手,一步步地完善一个空工程。这里就以修正后的例1.1.3为基础,进行完善。

步骤一:
再另外创建两个文件,其中一个源文件StdAfx.cpp,一个头文件StdAfx.h。(这两个文件的文件名可随意取,并不是固定的,我只是仿照了VC中自动创建的那两个文件,这样来得亲切,而且有助于理解“StdAfx”。)

步骤二:
       在每个头文件的首尾处添加条件编译命令,使得每个头文件中的代码都被夹在中间,像下面这样:
  1.   
  2. //***************************************   
  3. //*        头文件:CPoint.h                *   
  4. //*        描  述:点类CPoint的声明        *   
  5. //***************************************   
  6.   
  7. // 条件编译   
  8. #ifndef CPOINT_H   
  9. #define CPOINT_H   
  10.   
  11. class CPoint   
  12. {   
  13.     protected:   
  14.         double m_dX;   
  15.         double m_dY;   
  16.     public:   
  17.         CPoint(double x = 0, double y = 0);   
  18.         virtual void set(double x, double y);   
  19.         double getX();   
  20.         double getY();   
  21. };   
  22.   
  23. #endif  

  1.     
  2. //***************************************************************   
  3. //*        头文件:CLine.h                                                *   
  4. //*        描  述:线类CLine的声明,CLine由CPoint的派生而来。        *   
  5. //***************************************************************   
  6.   
  7. // 条件编译   
  8. #ifndef CLINE_H   
  9. #define CLINE_H   
  10.   
  11. class CLine:public CPoint   
  12. {   
  13.     protected:   
  14.         double m_dLength;   
  15.     public:   
  16.         CLine(double x = 0, double y = 0, double l = 0);   
  17.         void set(double x, double y, double l);   
  18.         double getLength();   
  19. };   
  20.   
  21. #endif  


       这些命令的作用是防止头文件重复包含。拿头文件CLine.h为例来稍微描述一下过程:当编译器在源文件(cpp文件)中第一次遇到头包含命令“#include “CLine.h””时,打开头文件CLine.h,准备对其在源文件中进行展开、编译。此时发现宏“CLINE_H”没有被定义,于是先定义这个宏,再将其后的代码进行编译,直到遇到“#endif”命令为止。当编译器在源文件中第二次遇到头包含命令“#include “CLine.h””时,再次打开头文件CLine.h,准备对其在源文件中进行展开、编译。然而此时发现宏“CLINE_H”已经被定义过了,于是后面的代码被跳过,不编译,直接跳至“#endif”命令。这样中间的代码就不会被再次编译了,也就不会出现重复定义的问题。

       另外补充一下,除了“#ifndef”和“#endif”这对条件编译命令外,还可以使用其他类似功能的条件编译指令。下面几种方式都可用于避免重复包含:
  1.   
  2. #ifndef MACRO   
  3. #define MACRO   
  4.   
  5. // ……   
  6. // 代码   
  7. // ……   
  8.   
  9. #endif  

  1.   
  2. #if !define(MACRO)   
  3. #define MACRO   
  4.   
  5. // ……   
  6. // 代码   
  7. // ……   
  8.   
  9. #endif  

  1.   
  2. #pragma once   
  3.   
  4. // ……   
  5. // 代码   
  6. // ……  


       前两种是等价的,后一种中“#pragma once”的意思是:在编译一个源文件时,只对该文件包含(打开)一次。至于它于前两种的区别,不是本文主题所在,这里就不讲了。

步骤三:
       删除所有文件(源文件和头文件)中的头包含命令。并在头文件StdAfx.h中包含当前工程所需要的所有头文件。如下:
  1.   
  2. //*******************************   
  3. //*        头文件:StdAfx.h        *   
  4. //*        描  述:包含所有头文件        *   
  5. //*******************************   
  6.   
  7. // 条件编译   
  8. #ifndef STDAFX_H   
  9. #define STDAFX_H   
  10.   
  11. // 包含工程所需所有头文件   
  12. #include    
  13. #include    
  14. using namespace std;   
  15. #include "CPoint.h"   
  16. #include "CLine.h"   
  17. #include “test.h”   
  18.   
  19. #endif STDAFX_H  


步骤四:
       在所有源文件(cpp文件或者c文件)首部包含头文件StdAfx.h,且仅包含这个头文件。

       到此,工程已经可以很好的工作了,而且上面的四个步骤对于C语言和C++,对于其他编译环境(如gcc、g++,不只在vc中)都适用。我们来整体看一下我们修改后的各个文件:
  1.     
  2. //***************************************   
  3. //*        头文件:CPoint.h                *   
  4. //*        描  述:点类CPoint的声明        *   
  5. //***************************************   
  6.   
  7. // 条件编译   
  8. #ifndef CPOINT_H   
  9. #define CPOINT_H   
  10.   
  11. class CPoint   
  12. {   
  13.     protected:   
  14.         double m_dX;   
  15.         double m_dY;   
  16.     public:   
  17.         CPoint(double x = 0, double y = 0);   
  18.         virtual void set(double x, double y);   
  19.         double getX();   
  20.         double getY();   
  21. };   
  22.   
  23. #endif  

  1.   
  2. //***************************************************************   
  3. //*        头文件:CLine.h                                                *   
  4. //*        描  述:线类CLine的声明,CLine由CPoint的派生而来。        *   
  5. //***************************************************************   
  6.   
  7. // 条件编译   
  8. #ifndef CLINE_H   
  9. #define CLINE_H   
  10.   
  11. class CLine:public CPoint   
  12. {   
  13.     protected:   
  14.         double m_dLength;   
  15.     public:   
  16.         CLine(double x = 0, double y = 0, double l = 0);   
  17.         void set(double x, double y, double l);   
  18.         double getLength();   
  19. };   
  20.   
  21. #endif  

  1.     
  2. //*******************************   
  3. //*        头文件:StdAfx.h        *   
  4. //*        描  述:包含所有头文件        *   
  5. //*******************************   
  6.   
  7. // 条件编译   
  8. #ifndef STDAFX_H   
  9. #define STDAFX_H   
  10.   
  11. // 包含工程所需所有头文件   
  12. #include    
  13. #include    
  14. using namespace std;   
  15. #include "CPoint.h"   
  16. #include "CLine.h"   
  17. #include “test.h”   
  18.   
  19. #endif STDAFX_H  

  1.   
  2. //***************************************   
  3. //*        头文件:test.h                        *   
  4. //*        描  述:test.cpp中的函数声明        *   
  5. //***************************************   
  6.   
  7. #ifndef TEST_H   
  8. #define TEST_H   
  9.   
  10. void test();   
  11.   
  12. #endif  

  1.     
  2. //*******************************   
  3. //*        源文件:main.cpp        *   
  4. //*******************************   
  5.   
  6. #include "StdAfx.h"   
  7.   
  8. int main()   
  9. {   
  10.     CLine line;   
  11.   
  12.     line.set(1.2, 2.3, 3.4);   
  13.     cout << line.getX() << endl;   
  14.     cout << line.getY() << endl;   
  15.     cout << line.getLength() << endl;   
  16.     test();   
  17.     system(“pause”);   
  18.     return 0;   
  19. }  

  1.     
  2. //*******************************   
  3. //*        源文件:test.cpp        *   
  4. //*******************************   
  5.   
  6. #include "StdAfx.h"   
  7.   
  8. void test()   
  9. {   
  10.   
  11. CLine line;   
  12.   
  13.     line.set(2.4, 1.5, 4.7);   
  14.     cout << line.getX() << endl;   
  15.     cout << line.getY() << endl;   
  16.     cout << line.getLength() << endl;   
  17. }  

  1.   
  2. //*******************************   
  3. //*        源文件:StdAfx.cpp        *   
  4. //*******************************   
  5.   
  6. #include "StdAfx.h"  


二、神行太保——预编译头文件

1、剥开StdAfx的神秘面纱

       如果你潜意识里感觉有什么东西不太搭调的话,那么你直觉是正确的。到此为止,都看不出源文件StdAfx.cpp有何功用,难道它是多余的?其实不然,它在VC中可以发挥更大的作用,可以使得一个庞大的工程在一次漫长的编译过后,接下来的编译更畅快,大大提高编译效率。不过要发挥它的作用必须借助于VC的环境设置,并不是所有开发环境都支持该功能。
      
       下面是摘用“百度百科”的几段话来说明StaAfx的作用。如果没看明白也没关系,接下来不讲理论,只谈浅显的运用。

         “Microsoft C 和 C++ 编译器提供了用于预编译任何 C 或 C++ 代码(包括内联代码)的选项。利用此性能特性,可以编译稳定的代码体,将已编译状态的代码存储在文件中,以及在随后的编译中,将预编译的代码与仍在开发的代码结合起来。由于不需要重新编译稳定代码,因此后面每次编译的速度都要快一些。”

“    VC创建项目时自动创建的预编译头文件,在编译其他文件之前,VC先预编译此文件。头文件stdafx.h引入了项目中需要的一些通用的头文件,比如window.h等,在自己的头文件中包括stdafx.h就包含了那些通用的头文件。
  所谓头文件预编译,就是把一个工程(Project)中使用的一些MFC标准头文件(如Windows.H、Afxwin.H)预先编译,以后该工程编译时,不再编译这部分头文件,仅仅使用预编译的结果。这样可以加快编译速度,节省时间。

  预编译头文件通过编译stdafx.cpp生成,以工程名命名,由于预编译的头文件的后缀是“pch”,所以编译结果文件是projectname.pch。
  编译器通过一个头文件stdafx.h来使用预编译头文件。stdafx.h这个头文件名是可以在project的编译设置里指定的。编译器认为,所有在指令#include "stdafx.h"前的代码都是预编译的,它跳过#include "stdafx. h"指令,使用projectname.pch编译这条指令之后的所有代码。”

       如果你想从理论上了解StdAfx的作用的话,可以参阅以下文章:
《关于#include "stdafx.h"》
http://blog.csdn.net/magicsutra/archive/2007/10/24/1842301.aspx
《百度百科——stdafx》
http://baike.baidu.com/view/1499221.htm?fr=ala0_1

       如果你对上述参考阅读不感兴趣,那么接下来我们就来看一下如何使StdAfx.cpp发挥作用。

2、领头羊StdAfx.cpp——创建预编译头文件

步骤一:
       鼠标左键单击Visual C++6.0菜单栏上的“Project”(工程),在弹出的菜单中选择“Settings”(设置),弹出“Project Settings”(工程设置)对话框。另外也可以鼠标右键单击“FileView”(文件视图)中的项目图标,在弹出的菜单中选择“Settings”,同样也可以打开“Project Settings”对话框。

步骤二:
       在“Project Settings”对话框中选择“C/C++”选项卡。在该选项卡菜单中的“Category”(类别)列表框中选择“Precompiled Headers”(预编译头)项,出现对应选项。

步骤三:
       在“Precompiled Headers”的对应选项中选择“Use pecompiled hader file (.pch)”(使用预编译头文件)单选按钮。在对应的“Through header”(通过头文件)编辑框中输入我们前面创建的头文件StdAfx.h。(前面已经提到,这个头文件可以任意命名,所以输入时依你自己的情况而定。)


下载 (55.81 KB)
2009-12-26 17:20

步骤四:
       在“Project Settings”对话框左侧展开工程的文件树。接着展开“Source Files”(源文件)文件夹,选择我们之前创建的源文件StdAfx.cpp(该文件名依情况而定)。

步骤五:
       在“Project Settings”对话框右侧与源文件StdAfx.cpp 对应的选项中选择“C/C++”选项卡。在该选项卡菜单中的“Category”(类别)列表框中选择“Precompiled Headers”(预编译头)项,于是出现对应选项。

步骤六:
       在“Precompiled Headers”对应选项中选择“Create pecompiled hader file (.pch)”(创建预编译头文件)单选按钮。在对应的“Through header”(通过头文件)编辑框中输入我们前面创建的头文件StdAfx.h。



下载 (55.4 KB)
2009-12-26 17:20

步骤七:
       单击“OK”(确定)按钮,完成设置。

       此后便可使用预编译头文件,发挥StdAfx.cpp的作用。重新编译整个工程,在工程目录下的“Debug”文件夹下会发现一个以该工程命名的.pch文件,它便是预编译头文件。看得出,其实预编译头文件并不是.h头文件。

下载 (69.96 KB)
2009-12-26 17:20

需要注意的是,其后在对头文件StdAfx.h进行改动后,须将整个StdAfx.cpp再重新编译一次,以更新预编译头文件,否则可能在单独编译其他源文件时可能会报错!


三、篇后语
       不知我的描述是否让你明白,希望本文对你有所帮助。如果你大致明白上述文字的意思,那么不妨动手实践一下。实践过程中如果发现您的结果与本文有所出入,敬请指出,欢迎一起交流讨论。

        最后补充下我们鹏友的相关文章链接:

《【分享】预编译头文件,你知道多少》
http://www.rupeng.com/forum/thread-3703-1-1-uid2477.html
《visual C++头文件stdafx.h》
http://www.rupeng.com/forum/thread-1283-1-1-uid2477.html

        以上两篇都是好帖,可惜发帖时间比较早,当时咱还是菜鸟,以致石沉大海,在这里给顶起来!