当您有一组 .h 文件成为经典“戈尔迪安结”情况的牺牲品时,您会怎么做,在哪里 #include 一个 .h 意味着您最终包含了几乎所有文件?预防显然是最好的药物,但是当供应商(!)发货之前发生这种情况时,您会怎么做?
这是问题的扩展,这可能是更相关的问题——你是否应该首先尝试解开依赖关系?
当您有一组 .h 文件成为经典“戈尔迪安结”情况的牺牲品时,您会怎么做,在哪里 #include 一个 .h 意味着您最终包含了几乎所有文件?预防显然是最好的药物,但是当供应商(!)发货之前发生这种情况时,您会怎么做?
这是问题的扩展,这可能是更相关的问题——你是否应该首先尝试解开依赖关系?
我已经在一个已经分成许多库的 C++ 代码库上完成了这项工作(这是一个好的开始)。
我必须锻炼(或猜测)哪个库是最依赖的,它不依赖于代码库中的其他任何东西。然后我依次处理每个库。
我依次查看了每个模块(*.cpp 文件),并确保首先将其自己的头文件#included 并注释掉其余部分,然后我注释掉该头文件中的所有#includes,然后仅重新编译该模块让编译器告诉我需要什么。我会取消注释似乎需要的第一个标题,并审查那个,必要时递归。有趣的是,最终不需要多少标头。
如果只需要名称(因为您有指针或引用),请使用class name;
or struct name;
,这称为前向声明并避免 #include 头文件。
当您注释掉#includes 时,编译器非常有助于告诉您依赖关系是什么(您需要使用所有必须保持可移植性的编译器重新编译)。
有时我不得不在库之间移动模块,以便没有库对或库组相互依赖。
当您有机会时,您应该重构代码以减少太大的包含,但是假设您可以实现某种包内聚。如果你解开事物只是为了发现代码的每个用户无论如何都必须包含所有元素,最终结果是相同的。
另一种选择是使用#defines 来配置部分的开启和关闭。无论如何,对于现有的代码库,解决方案是朝着包内聚的方向发展。
阅读: http: //ivanov.files.wordpress.com/2007/02/sedpackages.pdf并研究与包凝聚力相关的问题。
我已经解开了这个结好几次,在维护系统以尽可能减少 .h 依赖项时,它通常很有帮助。有一些不错的工具可以生成依赖树(我当时正在使用 Klocwork)。
我发现的缺点是条件编译。有人可能会删除一个头文件,因为他们认为我们不需要它,但事实证明我们只是不需要它,因为 VxWorks 有一些搞砸的头文件......在 Solaris(或任何合理的 Posix 系统)上你确实需要它。
在大量精心组织的标题和包含所有内容的单个标题之间需要取得平衡。考虑标准 C 库;有一些大的头文件,比如<stdio.h>
,它声明了很多函数,但它们都与 I/O 相关。还有其他一些更杂的标题 - 特别是<stdlib.h>
。
戈达德太空飞行中心的 C 指南值得一试。
基本规则是每个头文件都应该声明由一组合适的(通常是小的)源文件提供的设施。设施和标题应该是独立的。也就是说,如果有人需要 header 中的代码"something.h"
,那么它应该是唯一必须添加到编译中的 header。如果头文件中没有声明需要的设施"something.h"
,那么它必须包含相关的头文件。例如,这可能意味着标题最终会包含在内<stddef.h>
,因为其中一个函数使用size_t
了 .
正如@quamrana 指出的那样,您可以在适当的时候对结构(不是类,因为问题标记为 C 而不是 C++)使用前向声明 - 这主要意味着当接口采用指针并且不需要知道结构的大小或任何成员。