Answers:
PC Lint对此非常有效,它也为您找到了其他各种愚蠢的问题。它具有可用于在Visual Studio中创建外部工具的命令行选项,但我发现Visual Lint加载项更易于使用。甚至Visual Lint的免费版本也有帮助。但是,请尝试一下PC-Lint。配置它使其不会给您太多警告会花费一些时间,但是您会惊讶于它的出现。
有一个新的基于Clang的工具,包括“使用什么”,旨在实现此目的。
免责声明!我使用的是商用静态分析工具(不是PC Lint)。免责声明!
简单的非解析方法存在几个问题:
1)过载设置:
重载函数可能具有来自不同文件的声明。删除一个头文件可能导致选择了不同的重载,而不是编译错误!结果将是语义上的无声更改,此后可能很难跟踪。
2)模板专长:
与重载示例类似,如果您对模板具有部分或明确的专长,则希望在使用模板时它们全部可见。主模板的专业化可能在不同的头文件中。删除带有专门化的标题不会导致编译错误,但是如果选择了该专门化,则可能导致不确定的行为。(请参阅:C ++函数模板专业化的可见性)
正如“ msalters”所指出的那样,对代码执行完整的分析还可以分析类的用法。通过检查如何通过文件的特定路径使用类,可以完全删除该类的定义(以及所有其相关性),或者至少将其移到更接近include中主要源的级别。树。
我不知道任何这样的工具,我过去曾经考虑过要编写一个工具,但事实证明,这是一个很难解决的问题。
假设您的源文件包含ah和bh;ah包含#define USE_FEATURE_X
和bh使用#ifdef USE_FEATURE_X
。如果#include "a.h"
已将其注释掉,则您的文件可能仍会编译,但可能无法达到您的期望。以编程方式检测到这一点并非易事。
无论使用哪种工具,都需要知道您的构建环境。如果啊看起来像:
#if defined( WINNT )
#define USE_FEATURE_X
#endif
然后USE_FEATURE_X
仅在定义了if的WINNT
情况下进行定义,因此该工具将需要知道编译器本身生成了哪些指令,以及哪些指令是在compile命令而非头文件中指定的。
像Timmermans一样,我对任何工具都不熟悉。但是我知道有一些程序员编写了Perl(或Python)脚本,试图一次注释掉每个包含行,然后编译每个文件。
看来现在Eric Raymond 拥有了一个工具。
Google的cpplint.py有一个“包含您使用的内容”规则(还有许多其他规则),但是据我所知,没有“ 仅包含您使用的内容”规则。即使这样,它还是有用的。
一般而言,如果您对该主题感兴趣,则可能需要查看Lakos的Large C ++ Software Design。这有些陈旧,但是会涉及很多“物理设计”问题,例如找到需要包含的标头的绝对最小值。我还没有真正在其他地方看到过这种讨论。
您可以使用C / C ++ Include File Dependencies Watcher构建包含图,并从视觉上找到不需要的包含。
如果您的头文件通常以
#ifndef __SOMEHEADER_H__
#define __SOMEHEADER_H__
// header contents
#endif
(而不是一次使用#pragma),您可以将其更改为:
#ifndef __SOMEHEADER_H__
#define __SOMEHEADER_H__
// header contents
#else
#pragma message("Someheader.h superfluously included")
#endif
并且由于编译器输出正在编译的cpp文件的名称,因此至少可以让您知道哪个cpp文件导致多次引入标头。
A.h
和且B.h
都C.h
包含A.h
和时B.h
,您需要包含和,因为您同时需要包含和,所以会包含C.h
两次,但这很好,因为编译器将第二次跳过它,如果没有,则必须记住总是C.h
在之前A.h
或B.h
最后包含更多无用的内容。
PC-Lint确实可以做到这一点。一种简单的方法是将其配置为仅检测未使用的包含文件并忽略所有其他问题。这非常简单-仅启用消息766(“模块中未使用头文件”),只需在命令行中包含选项-w0 + e766。
相同的方法也可以与相关消息一起使用,例如964(“模块中未直接使用头文件”)和966(“模块中未直接使用间接包含的头文件”)。
我在上周的博客文章http://www.riverblade.co.uk/blog.php?archive=2008_09_01_archive.xml#3575027665614976318中更详细地介绍了FWIW 。
如果您要删除不必要的#include
文件以减少构建时间,则最好使用cl.exe / MP,make -j,Xoreax IncrediBuild,distcc / icecream等并行化构建过程,以节省时间和金钱。
当然,如果您已经有一个并行的构建过程,并且仍在尝试加快它的速度,那么请#include
务必清理您的指令并删除那些不必要的依赖项。
添加以下#define中的一个或两个都将通常排除不必要的头文件,并且可能会大大缩短编译时间,尤其是在代码未使用Windows API函数的情况下。
#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
如果尚未使用,则使用预编译的头文件包含所有您不会更改的内容(平台头文件,外部SDK头文件或项目的静态已完成部分)将在构建时间上产生巨大差异。
http://msdn.microsoft.com/zh-CN/library/szfdksca(VS.71).aspx
此外,尽管对您的项目来说可能为时已晚,但将项目组织成多个部分,并且不将所有本地标头集中到一个大的主标头中,是一个好习惯,尽管这需要一些额外的工作。
如果您将使用Eclipse CDT,则可以尝试http://includator.com来优化您的包含结构。但是,Includator可能对VC ++的预定义包含并不足够了解,并且CDT尚未内置CDT以将VC ++与正确的包含一起使用。
最新的Jetbrains IDE CLion自动显示(灰色)当前文件中未使用的包含。
也可以从IDE中获取所有未使用的包含(以及函数,方法等)的列表。
也许有点晚了,但是我曾经发现一个WebKit perl脚本可以完成您想要的操作。我相信它需要一些调整(我不太熟悉perl),但是应该可以解决问题:
(这是一个旧的分支,因为中继不再具有该文件)
如果有您认为不再需要的特定标头(例如string.h),则可以注释掉包含,然后将其放在所有包含下面:
#ifdef _STRING_H_
# error string.h is included indirectly
#endif
当然,您的接口头可能会使用不同的#define约定来将其包含在CPP内存中。或者没有约定,在这种情况下,这种方法将行不通。
然后重建。有三种可能性:
一切正常。string.h不是关键的编译对象,可以删除其中的include。
错误之旅。不知何故间接包含了string.g您仍然不知道是否需要string.h。如果需要,您应该直接#include它(见下文)。
您还会遇到其他一些编译错误。string.h是必需的,并且不是间接包含的,因此,包含是正确的。
请注意,当您的.h或.c直接使用另一个.h时,依赖于间接包含几乎是一个错误:您实际上承诺,只要您使用的其他某些头文件要求您的代码,该代码将只需要该头文件,这可能不是你的意思。
在其他答案中提到的有关标题修改行为的警告,也适用于声明导致构建失败的内容的声明。