我现在已经编程了一段时间,而且我还有一件事我还没想到就是你需要#include
的时候。我知道你可以安全地使用另一个文件中声明的东西。但有时我发现我可以删除#include
并且所有内容仍然可以正常编译。据我所知,这是因为已经包含的其他文件包括外部定义。我有两种特殊情况需要了解以下行为:
假设我们有三个.h / .cc对:f1.h / .cc,f2.h / .cc和f3.h / .cc。如果f2.h / .cc包含f1.h且f3.h / .cc包含f2.h,则f3.h / .cc必须包含f1.h或者所有f1.h的定义都可见f3文件包含在f2中?
再次说我们有三个.h / .cc对:f1.h / .cc,f2.h / .cc和f3.h / .cc。如果f2包含f1且f2包含f1然后f3包含f1或f2,则f1和f2之间的“循环链接”会导致问题吗?
您是否了解我可以阅读的任何优秀资源,以便更好地了解在一个文件中包含某些内容会如何影响项目中的后续文件?
答案 0 :(得分:6)
没有什么可以做的。如果你使用某些东西,你必须包括声明你使用的东西的标题。关于唯一的例外是forward declaring类/结构或方法,如:
class myclass;
如果你只需要声明一个指针或类的引用。
您无法真正依赖其他标题,包括您需要的标题。任何一天,其他标题的维护者都会意识到他/她不再需要包含它并将其删除。
答案 1 :(得分:2)
问题1:我认为你所缺少的是“f2包含f1”和“f2 保证包含f1”之间的区别。这对于标准标头尤为重要,因为任何标准标头允许包含任何其他标头。因此,如果您依赖于在您的机器上工作的间接包含,那么您的代码可能无法在不同的C ++实现上进行编译。
如果您有一个库,其中“f2.h”的文档说明或暗示它包含“f1.h”,这意味着它将始终在所有兼容版本中,因此您可以依赖间接包含。您可以在使用库的一个组件的情况下执行此操作,该组件基本上依赖于该库的另一个组件,但其他组件可能被其他用户隔离使用。对于一个假设的例子,“xhtml_parser.h”可能合理地证明它提供了“xml_parser.h”中的所有定义,以及一些额外的定义。
问题2:嗯,你想改一下这个问题吗? “f2包括f1,f2包括f1”不是你的意思,也没有“循环链接”。如果您编写标题使得f1包含f2而f2包含f1,可能会导致问题,因为include不是“链接”,它几乎是对另一个头文件内容的剪切和粘贴。
因此,即使在f3进入图片之前,循环包含也可能存在问题:
f1.h
----
#ifndef f1_h_included
#define f1_h_included
#include "f2.h"
struct DerivedA : BaseA {};
struct BaseB {};
#endif
f2.h
----
#ifndef f2_h_included
#define f2_h_included
#include "f1.h"
struct BaseA {};
struct DerivedB : BaseB {};
#endif
无论你包括哪个“f1.h”和“f2.h”,这都不会编译。假设首先包含f1,预处理后的结果如下所示:
// contents of f2.h, pasted in at line 4 of f1.h
// (contents of f1.h on the circular include are ignored due to include guard)
struct BaseA {};
struct DerivedB : BaseB {};
// rest of f1.h
struct DerivedA : BaseA {};
struct BaseB {};
因此DerivedB指定了尚未定义的基类。反过来包括它们,与DerivedA相同的问题。
答案 2 :(得分:1)
你已经把它钉了很多。 为了使用我需要的输入/输出流 包含头文件。 如果您编写了一个支持大整数类型的bigint类并且已发送 这堂课给朋友。你的朋友需要在他的程序中包含它才能使用它。因此,当您的程序无法使用时,请添加一些内容。
答案 3 :(得分:1)
但有时我觉得我可以 删除一个#include,一切都会 仍然编译得很好。从我的角度来看 可以告诉这是因为其他文件 已被包括在内 外部定义。
正确。这只是因为运气,等等。
假设我们有三个.h / .cc对: f1.h / .cc,f2.h / .cc和f3.h / .cc。如果 f2.h / .cc包括f1.h和f3.h / .cc 包括f2.h是否有必要 f3.h / .cc包含f1.h或全部 f1.h的定义可见 它包含在f3文件中 F2?
你可能意味着 f1.h的声明,而不是 definitions ,尽管你可能有一些类和模板函数定义。
无论如何,答案是否定的,它永远不会是必要的。这些声明将是可见的。预处理程序指令只是简单的文本插入。一旦你想象出你的想法,扩展变得容易理解。
再次说我们有三个.h / .cc 对:f1.h / .cc,f2.h / .cc,和 f3.h / .CC。如果f2包括f1和f2 包括f1然后f3包括f1或 f2将是“循环联动” 而f1和f2会导致问题吗?
是的,可能。如果头文件的内容是正确的,Header guards可以缓解此问题。但是如果f2的内容依赖于f1的内容,反之亦然,那么你在代码中就会有一个循环依赖。这应该通过删除循环依赖关系using forward declarations来解决。
你知道有什么好的资源吗? 在线我可以阅读以便更好地理解 如何在一个文件中包含内容 影响后续文件 项目
我可以推荐these resources。
答案 4 :(得分:0)
首先简短回答,解释如下:
1)不需要在f3.h / .cc中声明#include“f1.h”,因为这会产生一个包含周期(不应该这样做)
2)在1-> 2,1-> 2-> 3中没有循环,如果使用包含保护,则即使只包括2次(#ifndef 2 #define 2 my2code #endif)
实现是特定于编译器的,但总的来说:在菱形包含中,一个好的编译器将展平包含路径并在最深层找到文件,这个文件包含在某处但不需要包含任何其他内容。然后它将不断包含那些包含的文件,并且仅依赖于已包含的文件。由于这可能会导致重复包含,因此您应始终只包含标题(.cc将从相同的目录链接而不显式包含),并使用包含保护来保护您的标题文件:
e.g。 myheader.h:
#ifndef _myheader_h_
#define _myheader_h_
int myglobal;
#endif
如果你在包含中有一个循环,它取决于编译器:要么试图找到最深层次要么失败要么选择,要么它不试图选择最深层次并包括在顺序中你给了你的档案;无论如何:如果你避免循环(你应该)并使用包含警卫,你就是安全的。