zoukankan      html  css  js  c++  java
  • C++预编译头文件

    许多初学 VC 的朋友也许都为那么一个问题困扰过:

        为什么所有的 cpp 都必须 #include "stdafx.h"

        也许请教了别的高手之后,他们会告诉你,这是预编译头,必须包含。可是,这到底
    是为什么呢?预编译头有什么用呢?

        这得从头文件的编译原理讲起。其实头文件并不神秘,它的全部作用,就是把自己的
    所有内容直接“粘贴”到相应的 #include 语句处。如果不相信的话,不妨做个实验,将
    一个 cpp 中的所有 #include 语句删掉,并将它包含的文件粘贴到相应的位置,你会发
    现,文件的编译和运行都完全没有受到影响。其实,编译器在编译你的程序的时候,所做
    的第一件事,也就是展开所有的 #include 语句和 #define 语句。

        头文件的出现,固然给书写程序带来了很大方便。可是到了 Windows 时代后,慢慢
    就呈现出一些问题了。几乎所有的 Windows 程序都必须包含 windows.h,而那个文件却
    硕大无比,将它展开后往所有文件中一粘贴,编译的时候立刻慢得像只蜗牛。

        到了 MFC 时代后,情况更为恶劣了。毕竟 C 风格的 Windows 头文件里面包含的还
    仅仅是函数定义和宏,编译难度不算太大,而 MFC 库里面的头文件可都是类声明啊!更
    何况,一个最简单的工程,都会生成大量的类,需要用到大量的函数。如果工程稍微复杂
    一些,编译难度可想而知!

        但是,人们惊奇地发现,虽然用到的头文件又多又杂,但是在一个工程中,总有那么
    一堆头文件,是几乎所有 cpp 都必须包含的。那么,可不可以把这些头文件提取出来,
    只编译一编,然后所有其它 cpp 就都能使用呢?没错,这就是预编译头的思想都由来!

        实践证明,使用了预编译头技术后,编译速度大大提高了。可以到你的工程目录下的
    Debug 或 Release 目录中看一看,里面有一个体积极为硕大的 .pch 文件,那就是传说
    中的“编译之后的预编译头”。

        使用了预编译头技术后,虽然带来了极大地方便,但也造成了一个问题:由于它假定
    预编译头中包含过的头文件会在所有 cpp 中使用,因此它在编译你的 cpp 的时候,就会
    将预编译头中已经编译完的部分加载到内存中。如果它突然发现你的 cpp 居然没有包含
    预编译头,它就会很郁闷,因为它不知道该如何将已编译完的部分从内存中请出去,整个
    编译过程就会失败。

        因此,如果你使用了预编译头技术,就必须在所有的 cpp 中包含预编译头。MFC 工
    程中为你建立了一个默认的预编译头 stdafx.h,如果你愿意,也可以在自己的工程中使
    用其它文件名作为你的预编译头,如果你觉得有必要。
     

     

    预编译头文件的使用  
    关键字:预编译,/Yu,/Yc,/Yx
    本文介绍VC6的预编译功能的使用,由于预编译详细使用比较的复杂,这里只介绍几个最重要的预编译指令: /Yu, /Yc,/Yx,/Fp。其它的详细资料可以参考:
          MSDN->Visual Studio D6.0Document -> Visual C++6.0 Document
             ->VC++ Programmer Guider ->Compiler and Linker
             ->Details->Creating Precompiled Header files

    预编译头的概念: 
    所谓的预编译头就是把一个工程中的那一部分代码,预先编译好放在一个文件里(通常是以.pch为扩展名的),这个文件就称为预编译头文件这些预先编译好的代码可以是任何的C/C++代码--------甚至是inline的函数,但是必须是稳定的,在工程开发的过程中不会被经常改变。如果这些代码被修改,则需要重新编译生成预编译头文件。注意生成预编译头文件是很耗时间的。同时你得注意预编译头文件通常很大,通常有6-7M大。注意及时清理那些没有用的预编译头文件。
    也许你会问:现在的编译器都有Time stamp的功能,编译器在编译整个工程的时候,它只会编译那些经过修改的文件,而不会去编译那些从上次编译过,到现在没有被修改过的文件。那么为什么还要预编译头文件呢?答案在这里,我们知道编译器是以文件为单位编译的,一个文件经过修改后,会重新编译整个文件,当然在这个文件里包含的所有头文件中的东西(.eg Macro, Preprocessor )都要重新处理一遍。VC的预编译头文件保存的正是这部分信息。以避免每次都要重新处理这些头文件。
    预编译头的作用:
    方法一:手动方法 
    根据上文介绍,预编译头文件的作用当然就是提高便宜速度了,有了它你没有必要每次都编译那些不需要经常改变的代码。编译性能当然就提高了。
    预编译头的使用:
         要使用预编译头,我们必须指定一个头文件,这个头文件包含我们不会经常改变的代码和其他的头文件,然后我们用这个头文件来生成一个预编译头文件(.pch文件)
     想必大家都知道 StdAfx.h这个文件。很多人都认为这是VC提供的一个“系统级别”的,编译器带的一个头文件。其实不是的,这个文件可以是任何名字的。我们来考察一个典型的由AppWizard生成的MFC Dialog Based 程序的预编译头文件。(因为AppWizard会为我们指定好如何使用预编译头文件,默认的是StdAfx.h,这是VC起的名字)。我们会发现这个头文件里包含了以下的头文件:
    #include <afxwin.h>         // MFC core and standard components
    #include <afxext.h>         // MFC extensions
    #include <afxdisp.h>        // MFC Automation classes
    #include <afxdtctl.h>             // MFC support for Internet Explorer 4 Common Controls
    #include <afxcmn.h>     
    这些正是使用MFC的必须包含的头文件,当然我们不太可能在我们的工程中修改这些头文件的,所以说他们是稳定的。
    那么我们如何指定它来生成预编译头文件。我们知道一个头文件是不能编译的。所以我们还需要一个cpp文件来生成.pch 文件。这个文件默认的就是StdAfx.cpp。在这个文件里只有一句代码就是:#include “Stdafx.h”。原因是理所当然的,我们仅仅是要它能够编译而已―――也就是说,要的只是它的.cpp的扩展名。我们可以用/Yc编译开关来指定StdAfx.cpp来生成一个.pch文件,通过/Fp编译开关来指定生成的pch文件的名字。打开project ->Setting->C/C++ 对话框。把Category指向Precompiled Header。在左边的树形视图里选择整个工程 (如图)
    (图1)
    在图中我们的Project Options(右下角的那个白的地方)可以看到 /Fp “debug/PCH.pch”,这就是指定生成的.pch文件的名字,默认的通常是 <工程名>.pch(我的示例工程名就是PCH)。
    然后,在左边的树形视图里选择StdAfx.cpp.如图:(图2)
    这时原来的Project Option变成了 Source File Option(原来是工程,现在是一个文件,当然变了)。在这里我们可以看到 /Yc开关,/Yc的作用就是指定这个文件来创建一个Pch文件。/Yc后面的文件名是那个包含了稳定代码的头文件,一个工程里只能有一个文件的可以有YC开关。VC就根据这个选项把 StdAfx.cpp编译成一个Obj文件和一个PCH文件。
       然后我们再选择一个其它的文件来看看,如图:
    在这里,Precomplier 选择了 Use ………一项,头文件是我们指定创建PCH 文件的stdafx.h
    文件。事实上,这里是使用工程里的设置,(如图1)/Yu”stdafx.h”。
       这样,我们就设置好了预编译头文件。也就是说,我们可以使用预编译头功能了。以下是注意事项:
    1):如果使用了/Yu,就是说使用了预编译,我们在每个.cpp文件的最开头,我强调一遍是最开头,包含 你指定产生pch文件的.h文件(默认是stdafx.h)不然就会有问题。如果你没有包含这个文件,就告诉你Unexpected file end. 如果你不是在最开头包含的,你自己试以下就知道了,绝对有很惊人的效果…..
    2)如果你把pch文件不小心丢了,根据以上的分析,你只要让编译器生成一个pch文件就可以了。也就是说把 stdafx.cpp(即指定/Yc的那个cpp文件)从新编译一遍就可以了。当然你可以傻傻的 Rebuild all。简单一点就是选择那个cpp文件,按一下Ctrl + F7就可以了。
    方法二。自动使用 
    很简单只要指定/YX就可以了。或者在上图中选择Automatic………就可以了。注意的事情是如果你指定了/Yc /Yu的话,/Yx是会被忽略的。前者的优先级别高一些。

  • 相关阅读:
    hdu 5007 水题 (2014西安网赛A题)
    hdu 1698 线段树(成段替换 区间求和)
    poj 3468 线段树 成段增减 区间求和
    hdu 2795 公告板 (单点最值)
    UVaLive 6833 Miscalculation (表达式计算)
    UVaLive 6832 Bit String Reordering (模拟)
    CodeForces 124C Prime Permutation (数论+贪心)
    SPOJ BALNUM (数位DP)
    CodeForces 628D Magic Numbers (数位DP)
    POJ 3252 Round Numbers (数位DP)
  • 原文地址:https://www.cnblogs.com/lidabo/p/3253942.html
Copyright © 2011-2022 走看看