使用前向声明解决循环依赖

时间:2019-05-03 23:10:27

标签: c circular-dependency

出于这个问题,请忽略与相互依赖性有关的软件设计问题,而应着重于技术方面。

请考虑两个头文件a.hb.h。文件a.h定义了b.h所需的某些结构。但是,b.h还定义了a.h所需的结构。

据我了解,解决此循环#include依赖关系的常用技术位于b.h中,而不是包含a.h,它将从{中声明一个结构定义。 {1}}。看起来像这样:

a.h

a.h

B.h

// No nice typedef
struct MyThing_t {
    // ...
};

据我了解,这应该可以编译并工作。

但是-为什么有效?编译器在编译// Other things omitted typedef struct MyThing_t MyThing; // ... proceed using MyThing 时如何知道名称MyThing_t?这是什么机制?

还-为什么我不应该在b.h中也包含typedef,以供使用a.h没有循环依赖关系的模块使用?

3 个答案:

答案 0 :(得分:2)

处理这种情况的最直接方法是在不同的标头中定义结构,例如basic.h或类似的标头,并让a.hb.h都包含该标头。因为在b.h

typedef struct MyThing_t MyThing

前向声明声明了一个 opaque 类型,该类型仅允许您使用MyThing*指针。因为b.h不知道结构的实际大小或内存布局。这实际上是一个非常常见的C习惯用法,因为它允许封装结构的内部结构,并允许在对该指针进行操作的函数的头文件中进行声明,而无需了解实现。

答案 1 :(得分:0)

它是typedef *struct* MyThing_t MyThing;struct Mything_t不需要声明(这可能会导致错误,因此,对于所有内容使用typedefs是个好主意)。

我发现最好编写一个包含所有typedef的fwd.h文件(或对于大型项目,每个目录),并将其包含在所有其他标头的开头。

偶然地,除非为POSIX工作,否则应避免使用以_t结尾的名称。

答案 2 :(得分:0)

  

据我了解,这应该可以编译并工作。

是,不是。每个头都可以独立于源文件中的#include d,但是B.h不提供struct MyThing_t的定义。在范围内没有定义的地方,这是一个不完整的类型,这极大地限制了您可以使用它进行的操作。

  

但是-为什么有效?编译器如何知道名称   MyThing_t何时编译b.h?

如果在到达struct MyThing_t之前尚未意识到typedef,则typedef 使其意识到。

  

这里的机制是什么?

带有标签的结构类型可以在没有事先声明或定义的情况下使用,但只能以不完整类型使用的方式使用。这就是允许结构包含指向其自身类型的对象的指针的原因。它通常可以用于声明指向所讨论类型的指针,也可以在typedef中使用。另一方面,不完整的类型不能以任何需要知道其存储大小或(对于结构和联合类型)其成员的名称或类型的知识来使用。

  

也-为什么我不应该在a.h中也包含typedef,以供   使用a.h没有循环依赖关系的模块?

我不知道,你为什么不呢?这将比拆分它们更典型。不过,您确实需要注意两次声明相同的typedef。在最新版本的C中这样做是合法的,但是C90和C99不允许这样做。