【问题标题】:Is there a way to use pre-compiled headers in VC++ without requiring stdafx.h?有没有办法在 C++ 中使用预编译的头文件而不需要 stdafx.h?
【发布时间】:2010-09-22 08:19:41
【问题描述】:

我有一堆需要编写单元测试的遗留代码。它在任何地方都使用预编译的头文件,因此几乎所有 .cpp 文件都依赖于 stdafx.h,这使得为了编写测试而打破依赖关系变得困难。

我的第一反应是删除所有这些 stdafx.h 文件,这些文件大部分都包含 #include 指令,并根据需要将这些 #include 直接放在源文件中。

这将使得有必要关闭预编译头文件,因为它们依赖于 stdafx.h 之类的文件来确定预编译头文件的停止位置。

有没有办法在没有 stdafx.h 依赖的情况下保留预编译的头文件?有没有更好的方法来解决这个问题?

【问题讨论】:

  • 我个人使用预编译头文件(用于普通代码和单元测试),但使用名称 Precompiled.h/cpp。单元测试的预编译头文件包括额外的头文件,以便编译更快。
  • 是的,有更好的方法。每个源文件只能使用一个预编译头文件,但可以为 每个 源文件或一组源文件指定 pch。您还可以指定哪些源文件使用或不使用预编译头文件。请参阅下面的答案。

标签: c++ unit-testing dependencies precompiled-headers stdafx.h


【解决方案1】:

预编译的头文件可以在重建项目时节省大量时间,但是如果预编译的头文件发生变化,每个依赖于头文件的源文件都会被重新编译,无论变化是否影响它。幸运的是,预编译的头文件用于编译,而不是链接每个源文件都不必使用相同的预编译头文件。

pch1.h:

#include <bigHeader1.h>
#include ...


pch1.cpp:

#include "pch1.h"


source1.cpp:

#include "pch1.h"
[code]


pch2.h:

#include <bigHeader2.h>
#include ...


pch2.cpp:

#include "pch2.h"


source2.cpp

#include "pch2.h"
[code]

选择pch1.cpp,右击,Properties,Configuration Properties,C/C++,Precompiled Headers
预编译头文件: 创建(/Yc)
预编译头文件: pch1.h
预编译头输出文件:$(intDir)pch1.pch

选择source1.cpp
预编译头文件:使用(/Yu)
预编译头文件: pch1.h
预编译的头文件输出文件:$(intDir)pch1.pch(我认为这对 /Yu 无关紧要)

pch2.cppsource2.cpp做同样的事情,除了设置头文件头输出文件 到 pch2.hpch2.pch。这对我有用。

【讨论】:

    【解决方案2】:

    是的,有更好的方法。

    恕我直言,预编译头文件的“向导风格”的问题在于,它们鼓励不必要的耦合,并使代码重用变得比应有的困难。此外,使用“将所有内容都粘贴到 stdafx.h”样式编写的代码很容易维护,因为更改任何头文件中的任何内容都可能导致整个代码库每次都重新编译。这会使简单的重构变得很耗时,因为每次更改和重新编译周期都比它应该花费的时间长得多。

    恕我直言,更好的方法是使用#pragma hdrstop 和/Yc 和/Yu。这使您可以轻松设置使用预编译头文件的构建配置,也可以构建不使用预编译头文件的配置。使用预编译头文件的文件与源文件中的预编译头文件本身没有直接依赖关系,这使得它们可以在使用或不使用预编译头文件的情况下进行构建。项目文件确定哪个源文件构建预编译头文件,每个源文件中的#pragma hdrstop 行确定哪些包含来自预编译头文件(如果使用),哪些直接来自源文件......这意味着当进行维护时,您将使用不使用预编译头文件的配置,并且只有在头文件更改后需要重新构建的代码才会重新构建。在进行完整构建时,您可以使用预编译的头文件配置来加快编译过程。使用非预编译头文件构建选项的另一个好处是,它确保您的 cpp 文件只包含他们需要的内容并包含他们需要的所有内容(如果您使用预编译头文件的“向导风格”,这将很难做到。

    我在这里写了一些关于它是如何工作的:http://www.lenholgate.com/blog/2004/07/fi-stlport-precompiled-headers-warning-level-4-and-pragma-hdrstop.html(忽略关于 /FI 的东西),我在这里有一些使用 #pragma hdrstop 和 /Yc /Yu 方法构建的示例项目:http://www.lenholgate.com/blog/2008/04/practical-testing-16---fixing-a-timeout-bug.html .

    当然,从“向导风格”的预编译标头使用转变为更受控制的风格通常并非易事......

    【讨论】:

    【解决方案3】:

    预编译头文件的前提是所有内容都将包含相同的内容集。如果您想使用预编译的头文件,那么您必须忍受这暗示的依赖关系。它归结为依赖关系与构建速度的权衡。如果您可以在关闭预编译头文件的情况下在合理的时间内构建,那么一定要这样做。

    要考虑的另一件事是每个库可以有一个 pch。因此,您可以将代码拆分为更小的库,并让每个库都有更紧密的依赖关系。

    【讨论】:

      【解决方案4】:

      是的。 “stdafx.h/stdafx.pch”名称只是约定俗成的。您可以为每个 .cpp 提供自己的预编译头文件。这可能是最容易通过一个小脚本来编辑 .vcproj 中的 XML 来实现的。缺点:你最终会得到一大堆预编译的头文件,并且它们不会在 TU 之间共享。

      可能,但很聪明?我不能确定。

      【讨论】:

        【解决方案5】:

        当您通常使用预编译头文件时,“stdafx.h”有两个用途。它定义了一组稳定的、通用的包含文件。同样在每个 .cpp 文件中,它用作预编译头文件结束位置的标记。

        听起来你想做的是:

        • 保持预编译头打开。
        • 将“stdafx.h”包含在每个 .cpp 文件中。
        • 从“stdafx.h”中清空包含。
        • 对于每个 .cpp 文件,从旧的“stdafx.h”中找出需要哪些包含。在每个 .cpp 文件中的 #include "stdafx.h" 之前添加这些内容。

        所以现在您拥有最少的依赖集,并且您仍在使用预编译的头文件。损失是您不会只预编译您的公共标头集一次。对于全面重建来说,这将是一个巨大的打击。对于一次只重新编译几个文件的开发模式,它的影响较小。

        【讨论】:

        • 谢谢。我决定采用这种方法的一种变体。 stdafx.h 只允许包含外部库的#includes(windows 头文件、STL、boost 等)。所有内部应用程序头文件必须直接包含在 .cpp 文件中。
        【解决方案6】:

        我只对需要包含 afx___ 内容的代码使用预编译的标头 - 通常只是 UI,我不对其进行单元测试。 UI 代码处理 UI 并调用具有单元测试的函数(尽管大多数当前没有,因为该应用是旧版)。

        对于大部分代码,我不使用预编译的标头。

        G.

        【讨论】:

          【解决方案7】:

          我的建议是 - 不要删除预编译的头文件,除非你想让你的构建变得非常缓慢。您在这里基本上有三个选择:

          1. 摆脱预编译头文件(不推荐)
          2. 为遗留代码创建一个单独的库;这样您就可以单独构建它。
          3. 在单个项目中使用多个预编译头文件。您可以在解决方案资源管理器中选择单个 C++ 文件,并告诉他们要使用哪个预编译头文件。您还需要设置您的 OtherStdAfx.h/cpp 以生成预编译的标头。

          【讨论】:

            【解决方案8】:

            没有。预编译的头文件依赖于以这种方式编译的所有源包含的单个头文件。 您可以为单个源(或全部)指定根本不使用预编译的标头,但这不是您想要的。

            过去,Borland C++ 编译器在没有特定头文件的情况下进行预编译。但是,如果两个源文件包含相同的头文件但 order 不同,则它们将分别编译,因为确实,C++ 中头文件的顺序很重要......

            因此,这意味着 borland 预编译头文件只有在您非常严格地以相同顺序包含源代码或所有其他文件(首先)包含一个包含文件时才节省时间...... - 听起来很熟悉?! ?!

            【讨论】:

            • 不正确;你可以为每个单独的源文件指定一个不同的预编译头文件,或者指定一些源文件使用一个而其他的不使用。
            【解决方案9】:

            不,可能没有更好的方法。

            但是,对于给定的单个 .cpp 文件,您可能会决定不需要预编译的标头。您可以修改该 .cpp 文件的设置并删除 stdafx.h 行。

            (但实际上,我不知道预编译的标头方案如何干扰单元测试的编写)。

            【讨论】:

            • 是的,有更好的方法。请看下面我的回答。 @Len Holgate 的回答也有效。
            猜你喜欢
            • 1970-01-01
            • 2019-01-26
            • 1970-01-01
            • 2012-12-02
            • 2020-07-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-06-20
            相关资源
            最近更新 更多