我有一堆遗留代码,我需要编写单元测试。它在任何地方使用预编译的头文件,因此几乎所有.cpp文件都依赖于stdafx.h,这使得很难打破依赖关系以便编写测试。
我的第一直觉是删除所有这些stdafx.h文件,这些文件大部分都包含#include指令,并根据需要将#includes直接放在源文件中。
这将使得有必要关闭预编译的头文件,因为它们依赖于像stdafx.h这样的文件来确定预编译头文件的停止位置。
有没有办法保留没有stdafx.h依赖项的预编译头文件?有没有更好的方法来解决这个问题?
答案 0 :(得分:9)
是的,还有更好的方法。
问题,恕我直言,预编译标题的'向导样式'是它们鼓励不必要的耦合并使代码重用比应有的更难。此外,使用“只是坚持stdafx.h中的所有内容”样式编写的代码很容易维护,因为更改任何头文件中的任何内容都可能导致整个代码库每次都重新编译。这可以使简单的重构永远,因为每次更改和重新编译周期都需要更长的时间。
恕我直言,更好的方法是使用#pragma hdrstop和/ Yc和/ Yu。这使您可以轻松设置使用预编译头的构建配置,还可以构建不使用预编译头的配置。使用预编译头文件的文件与源文件中预编译头文件本身没有直接依赖关系,这使得它们可以使用或不使用预编译头进行构建。项目文件确定哪个源文件构建预编译头文件,并且每个源文件中的#pragma hdrstop行确定哪些包含从预编译头文件(如果使用)中获取,哪些包含直接从源文件中获取...这意味着当进行维护时,您将使用不使用预编译头的配置,并且只有在头文件更改后重建所需的代码才会重建。完成构建时,您可以使用预编译的头配置来加速编译过程。使用非预编译的头部构建选项的另一个好处是,它确保您的cpp文件仅包含他们需要的内容并包含他们需要的所有内容(如果您使用预编译头的'向导样式',则很难。< / p>
我已经在这里写了一些关于它是如何工作的:http://www.lenholgate.com/blog/2004/07/fi-stlport-precompiled-headers-warning-level-4-and-pragma-hdrstop.html(忽略关于/ FI的东西)我有一些用#pragma hdrstop和/ Yc / Yu方法构建的示例项目:{ {3}}。
当然,从“向导样式”预编译的头文件使用到更加受控的样式通常是非常重要的......
答案 1 :(得分:4)
当您通常使用预编译头时,“stdafx.h”有两个目的。它定义了一组稳定的,通用的包含文件。同样在每个.cpp文件中,它用作预编译头文件结束位置的标记。
听起来你想做的是:
现在您拥有最小的依赖项集,并且您仍在使用预编译的标头。遗失的是,您不是只预编译一组常用标题。这对于完全重建来说将是一个巨大的打击。对于开发模式,您只需要一次重新编译一些文件,它就不那么受欢迎了。
答案 2 :(得分:2)
不,可能 NOT 是一种更好的方式。
但是,对于给定的单个.cpp文件,您可能认为不需要预编译头。您可以修改该.cpp文件的设置并删除stdafx.h行。
(实际上,我不知道预编译的头部方案是如何干扰单元测试的写入的。)
答案 3 :(得分:2)
没有。预编译的头文件依赖于以这种方式编译的所有源包含的单个头。 您可以指定单个源(或所有)根本不使用预编译的头,但这不是您想要的。
过去,Borland C ++编译器在没有特定标头的情况下进行了预编译。但是,如果两个源文件包含相同的标题但不同的顺序,则它们是单独编译的,因为实际上,C ++中头文件的顺序可能很重要......
因此,这意味着borland预编译的标题确实可以节省时间,只有当您非常严格地包含相同顺序的源,或者所有其他文件包含(第一个)单个包含文件时... - 听起来很熟悉? ?!
答案 4 :(得分:2)
是。 “stdafx.h / stdafx.pch”名称只是惯例。您可以为每个.cpp提供自己的预编译头。通过一个小脚本来编辑.vcproj中的XML可能最容易实现。缺点:您最终会得到一大堆预编译的标题,并且它们不会在TU之间共享。
可能,但很聪明?我不能肯定地说。
答案 5 :(得分:1)
我的建议是 - 除非你想让你的构建变得非常缓慢,否则不要删除预编译的头文件。你基本上有三个选择:
答案 6 :(得分:1)
预编译标题的前提是所有内容都包含相同的内容。如果您想使用预编译的头文件,那么您必须使用这意味着的依赖项。它归结为依赖关系与构建速度的权衡。如果你可以在一个合理的时间内建立关闭预编译的标题,那么一定要做。
另一件需要考虑的事情是每个库可以有一个pch。因此,您可以将代码拆分为较小的库,并使每个库都具有更严格的依赖关系。
答案 7 :(得分:0)
我只对需要包含afx___内容的代码使用预编译头文件 - 通常只是UI,我不进行单元测试。 UI代码处理UI并调用具有单元测试的函数(尽管目前大部分都不是由于应用程序是遗留的)。
对于大部分代码,我不使用预编译的头文件。
-G。
答案 8 :(得分:0)
预编译头可以在重建项目时节省大量时间,但如果预编译头更改,则将重新编译每个源文件(取决于头),无论更改是否影响它。幸运的是,预编译头文件用于编译,而不是 link ; 每个源文件都不必使用相同的预编译标头。
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.cpp 和 source2.cpp 做同样的事情,除了设置标题文件和标题输出文件< / strong>到 pch2.h 和 pch2.pch 。这对我有用。