在C/C++中检测多余的#includes?

sho*_*osh 272 c c++ refactoring dependencies include

我经常发现文件的标题部分一直变得越来越大但它永远不会变小.在源文件的整个生命周期中,类可能已经移动并被重构,很可能有很多#includes不需要在那里再存在.将它们留在那里只会延长编译时间并增加不必要的编译依赖性.试图弄清楚哪些仍然需要可能是相当繁琐的.

是否有某种工具可以检测多余的#include指令并建议哪些可以安全删除?
lint可能这样做吗?

Jos*_*ley 162

谷歌的cppclean(链接到:下载,文档)可以找到几类C++问题,它现在可以找到多余的#includes.

还有一个基于Clang的工具,包括你可以使用的,可以做到这一点.包括你使用什么甚至可以建议前向声明(所以你没有#include这么多)并可选择为你清理你的#includes.

Eclipse CDT的当前版本也内置了此功能:在Source菜单下,单击Organize Includes将按字母顺序排列#include,添加Eclipse认为您正在使用的任何标头而不直接包含它们,并注释掉它所有的标题你认为你不需要.但是,此功能并非100%可靠.

  • 我更新了维护的cppclean fork的链接:https://github.com/myint/cppclean (3认同)
  • 它现在.我刚开始使用它.请看我的笔记.http://stackoverflow.com/questions/1301850/tools-to-find-included-headers-which-are-unused/7111685#7111685 (2认同)

Tza*_*rir 61

另外,请查看包括您使用的内容,它可以解决类似的问题.

  • 恕我直言这个答案需要更多的支持,因为一旦解决了问题,谷歌的IWYU工具将成为这项任务的权威工具. (6认同)
  • sudo apt-get install iwyu (4认同)

小智 42

它不是自动的,但doxygen将生成#included文件的依赖关系图.你必须在视觉上仔细阅读它们,但它们对于获得正在使用什么的图片非常有用.

  • @Tom:这是一个可怕的想法:对于一个它没有显示这些包含是否需要,其次,包含列表不应该依赖于未来可能发生变化的间接包含(冗余包括通常不是这样的无论如何,感谢包括警卫和编译器魔法,但在文件中实际使用了哪些类/函数(您的编译器不应该经历数千行模板代码甚至无法实例化) (30认同)
  • 这是查看链的好方法..看到A - > B - > C - > D和A - > D立即显示冗余. (5认同)
  • 你是如何制作Doxygen显示依赖关系图的? (3认同)

Jar*_*Par 24

检测多余包含的问题在于它不能仅仅是类型依赖性检查器.一个多余包括为它提供了什么有价值的汇编文件,并不会改变它的其他文件依赖其他项目.头文件可以通过多种方式改变编译,例如通过定义常量,重新定义和/或删除已使用的宏,添加一个命名空间,该命名空间可以在某种程度上改变名称的查找.为了检测命名空间之类的项目,您需要的不仅仅是预处理器,实际上您几乎需要一个完整的编译器.

Lint更像是一种风格检查器,当然不具备这种全部功能.

我想你会发现检测多余包含的唯一方法是删除,编译和运行套件.

  • 如果包含文件布局良好,这些都不会成为问题.如果您需要在文件B之前包含文件A,那么您做错了(我已经在他们做错的项目上工作过). (8认同)
  • @David,是的,但这取决于开发人员的年数,然后才能正确地完成它.我可以肯定地说,发生这种情况的可能性有利于房子,而不是你:( (8认同)
  • @Benoit,那么您将忽略一类编译但在语义上改变程序含义的问题。考虑一个文件中的 #define 如何改变另一个文件中的 #if 分支。删除标头仍然可以使其编译出不同的结果 (2认同)

its*_*att 15

我认为PCLint可以做到这一点,但是我已经看了几年了.你可以看一下.

我查看了这个博客,作者谈了一些关于配置PCLint以查找未使用的包含的内容.也许值得一瞧.

  • 我经常使用PCLint,它会告诉我未使用的标题.我小心地将#include标题注释掉并重新编译以确保标题真的未被使用... (4认同)
  • 太贵了.不是群众可行的工具. (4认同)

Dio*_*lis 7

所述CScout重构浏览器可以在C(不幸的是没有C++)码检测多余包括指令.您可以在期刊文章中找到有关其工作原理的说明.


Gil*_*aor 6

您可以编写一个快速脚本来擦除单个#include指令,编译项目,并在没有编译错误的情况下将名称记录在#include中,并将其从中删除.

让它在夜间运行,第二天你将有一个100%正确的包含文件列表,你可以删除.

有时蛮力只是起作用:-)


编辑:有时不会:-).以下是评论中的一些信息:

  1. 有时您可以单独删除两个头文件,但不能同时删除两个头文件.解决方案是在运行期间删除头文件而不是将它们带回来.这将找到一个可以安全删除的文件列表,尽管可能有一个解决方案包含更多要删除的文件,但该算法无法找到.(这是对要删除的包含文件空间的贪婪搜索.它只能找到本地最大值)
  2. 如果您根据某些#ifdefs重新定义了一些宏,则行为可能会有细微的变化.我认为这些是非常罕见的情况,作为构建的一部分的单元测试应该捕获这些变化.

  • 馊主意。如果一个头文件 #defines 一个常量 BLAH 并且另一个头文件检查 #ifdef BLAH,则删除第一个头文件可能仍会成功编译,但您的行为已更改。 (3认同)
  • 请注意这一点 - 假设有两个头文件,它们都包含某些内容的定义。您可以删除其中一个,但不能同时删除两者。您需要更彻底地使用暴力方法。 (2认同)
  • 这也可能会导致系统标头出现问题,因为不同的实现可能在 #include <vector> 中包含不同的内容。即使您坚持使用一种编译器,标头也可能会因不同版本而发生变化。 (2认同)
  • 如果您要包含一个包含您真正需要的标题的标题,则不会发现这种情况. (2认同)

Dan*_*Dan 5

抱歉(重新)发布在这里,人们通常不会扩展评论.

检查我对crashmstr的评论,FlexeLint/PC-Lint会为你做这个.信息性消息766.我的手册(版本8.0)的第11.8.1节讨论了这一点.

此外,这很重要,继续迭代直到消息消失.换句话说,在删除未使用的标头后,重新运行lint,一旦删除一些不需要的标头,更多的头文件可能已经变得"不需要".(这可能听起来很傻,慢慢阅读并解析它,这很有道理.)


Dan*_*son 5

我从来没有找到一个完整的工具来完成你所要求的.我使用的最接近的东西是IncludeManager,它绘制了您的标题包含树的图形,这样您就可以在视觉上发现仅包含在一个文件和圆形标题内容中的标题.