在VC++中是否有一种使用预编译头文件的方法,而不需要使用stdafx.h?

11

我有一堆需要编写单元测试的遗留代码。由于几乎所有的.cpp文件都依赖于stdafx.h,这使得破坏依赖关系以编写测试变得非常困难。

我的第一反应是删除所有这些stdafx.h文件,因为它们大多只包含#include指令,然后根据需要直接将这些#includes放置在源文件中。

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

是否有一种方法可以保持预编译头文件而没有stdafx.h的依赖关系?是否有更好的解决此问题的方法?


个人而言,我使用预编译头文件(用于普通代码和单元测试),但是将其命名为Precompiled.h/cpp。单元测试的预编译头文件包括额外的头文件,以便编译速度更快。 - Dominik Grabiec
请投票支持此建议:http://visualstudio.uservoice.com/forums/121579-visual-studio/suggestions/4931119-allow-precompiled-headers-to-be-shared-between-pro - Scott Langham
是的,有更好的方法。每个源文件只能使用一个预编译头文件,但可以为每个源文件或一组源文件指定pch。您还可以指定哪些源文件使用或不使用预编译头文件。请参见下面的我的答案。 - riderBill
9个回答

9
是的,有更好的方法。
在我看来,“向导风格”的预编译头存在问题,它们会鼓励不必要的耦合,并使重用代码变得比应该更困难。此外,使用“把所有东西都放到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
当然,从“向导风格”预编译头使用到更受控制的样式通常是不容易的...

请参见:https://dev59.com/Y2w05IYBdhLWcg3wXQsg - 谢谢。 - Martin Ba

4
当您通常使用预编译头文件时,“stdafx.h”有两个作用。它定义了一组稳定的、常见的包含文件。在每个.cpp文件中,它还作为预编译头文件结束位置的标记。
听起来您想要做的是:
- 保留预编译头文件打开。 - 在每个.cpp文件中保留“stdafx.h”包含文件。 - 清空“stdafx.h”中的包含内容。 - 对于每个.cpp文件,找出旧的“stdafx.h”所需的包含文件。将这些添加到每个.cpp文件中#include“stdafx.h”的前面。
现在您拥有最小的依赖关系,并且仍在使用预编译头文件。损失在于您不仅一次性预编译常见的头文件。对于完全重建,这将是一个巨大的打击。对于开发模式,在那里您只重新编译一些文件,它的影响会较小。

谢谢。我决定采用这种方法的变体。 stdafx.h 只允许包含外部库的 #include(如 windows headers、STL、boost 等)。所有内部应用程序标头必须直接包含在 .cpp 文件中。 - Ferruccio

2

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

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

(实际上,我不知道预编译头文件方案如何干扰到你编写单元测试的过程)。


是的,有更好的方法。请看下面我的回答。@Len Holgate的回答也可以。 - riderBill

2
没有预编译头文件依赖于所有以这种方式编译的源文件都包含的单个头文件。您可以指定单个源文件(或全部)不使用预编译头文件,但这不是您想要的。过去,Borland C++编译器在没有特定头文件的情况下进行预编译。然而,如果两个源文件包含了相同的头文件,但是顺序不同,它们会被单独编译,因为在C++中头文件的顺序确实很重要...因此,这意味着Borland预编译头文件只有在非常严格地按照相同顺序包含源文件或者有一个单一的头文件被所有其他文件包含(首先)时才能节省时间...听起来很熟悉吧?!?

不是这样的;您可以为每个单独的源文件指定不同的预编译头文件,或者指定某些源文件使用一个而其他源文件不使用。 - riderBill

2

是的,"stdafx.h/stdafx.pch" 只是一种约定俗成的命名。你可以为每个 .cpp 文件创建自己的预编译头文件。最简单的方法可能是通过一个小脚本编辑你的 .vcproj 文件中的 XML。缺点是你会得到一大堆预编译头文件,并且它们不能在 TU 之间共享。

这样做是可行的,但是否明智我无法确定。


1

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

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

1

预编译头文件基于所有内容都将包含相同的设置的想法。如果您要利用预编译头文件,则必须接受这意味着的依赖关系。这涉及到依赖关系与构建速度之间的权衡。如果您可以在关闭预编译头文件的情况下在合理的时间内构建,则一定要这样做。

另一个要考虑的事情是您可以为每个库拥有一个pch。因此,您可能可以将代码分成较小的库,并使它们中的每一个具有更紧密的依赖关系集。


0

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

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,右键点击,属性,配置属性,C/C++,预编译头文件
预编译头文件:创建(/Yc)
预编译头文件名:pch1.h
预编译头文件输出:$(intDir)pch1.pch

选择 source1.cpp
预编译头文件:使用(/Yu)
预编译头文件名:pch1.h
预编译头文件输出:$(intDir)pch1.pch (我认为这对于/Yu没有影响)

对于 pch2.cppsource2.cpp 做同样的事情,只需将头文件头文件输出设置为 pch2.hpch2.pch。这对我有效。


0

我只在需要包含afx___相关内容的代码中使用预编译头文件,通常只有UI部分不需要进行单元测试。UI代码处理UI并调用具有单元测试的函数(尽管由于应用程序是遗留代码,大多数函数目前没有单元测试)。

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

G.


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接