c++ - 什么是循环包含依赖项,为什么不好,我该如何解决?

标签 c++ c include

假设我有两个相互引用的数据结构。我想像这样将它们放入单独的头文件中:

 // datastruct1.h
 #ifndef DATA_STRUCT_ONE
 #define DATA_STRUCT_ONE

 #include <datastruct2.h>
 typedef struct DataStructOne_t
 {
   DataStructTwo* two;
 } DataStructOne;
 #endif

 // datastruct2.h
 #ifndef DATA_STRUCT_TWO
 #define DATA_STRUCT_TWO

 #include <datastruct1.h>
 typedef struct DataStructTwo_t
 {
   DataStructOne* one;
 } DataStructTwo;

 #endif

我有一个 main 函数:

 #include <datastruct1.h>
 #include <datastruct2.h>

 int main() 
 {
    DataStructOne* one;
    DataStructTwo* two;
 }

但是我的编译器提示:

$ gcc -I. -c main.c
In file included from ./datastruct1.h:4,
                 from main.c:1:
./datastruct2.h:8:2: error: unknown type name ‘DataStructOne’
    8 |  DataStructOne* one;
      |  ^~~~~~~~~~~~~

这是为什么呢?我该怎么做才能解决这个问题?

最佳答案

为什么?

为了理解原因,我们需要像编译器一样思考。让我们在分析 main.c 时这样做逐行。编译器会做什么?

  • #include <datastruct1.h> : 把“main.c”放在一边(压入正在处理的文件栈),切换到“datastruct1.h”
  • #ifndef DATA_STRUCT_ONE : 嗯,这还没有定义,让我们继续。
  • #define DATA_STRUCT_ONE : 好的,定义了!
  • #include <datastruct2.h> : 将“datastruct1.h”放在一边,切换到“datastruct2.h”
  • #ifndef DATA_STRUCT_TWO : 嗯,这还没有定义,让我们继续。
  • #define DATA_STRUCT_TWO : 好的,定义了!
  • #include <datastruct1.h> : 将“datastruct2.h”放在一边,切换到“datastruct1.h”
  • #ifndef DATA_STRUCT_ONE :现在已经定义,所以直接转到#endif .
  • (end of "datastruct1.h") :关闭“datastruct1.h”并从填充堆栈中弹出当前文件。我在做什么?啊,“datastruct2.h”。我们从离开的地方继续。
  • typedef struct DataStructTwo_t好的,开始结构定义
  • DataStructOne* one; 等等,什么是DataStructOne ? 我们没见过吧? (查看已处理行的列表)不,不 DataStructOne洞察力。 panic !

发生了什么事?为了编译“datastruct2.h”,编译器需要“datastruct1.h”,但是 #include “datastruct1.h”中的守卫阻止其内容实际包含在需要的地方。

情况是对称的,所以如果我们调换 #include 的顺序“main.c”中的指令,我们得到相同的结果,但两个文件的角色相反。我们也不能删除守卫,因为那样会导致无限文件包含链。

看来我们需要“datastruct2.h”出现在“datastruct1.h”之前并且我们需要“datastruct1.h”出现在“datastruct2.h”之前。这似乎不可能。

什么?

文件A的情况#include s文件B依次为#include s 文件 A 显然是 Not Acceptable 。我们需要打破恶性循环。

幸运的是,C 和 C++ 有前向声明。我们可以使用这个语言特性来重写我们的头文件:

 #ifndef DATA_STRUCT_ONE
 #define DATA_STRUCT_ONE

 // No, do not #include <datastruct2.h>
 struct DataStructTwo_t; // this is forward declaration

 typedef struct DataStructOne_t
 {
   struct DataStructTwo_t* two;
 } DataStructOne;
 #endif

在这种情况下,我们可以用同样的方式重写“datastruct2.h”,消除它对“datastruct1.h”的依赖,在两个处打破循环(严格来说,这是不需要的,但较少的依赖总是好的)。唉。这并非总是如此。通常只有一种方法可以引入前向声明并打破循环。例如,如果,而不是

DataStructOne* one;

我们有

DataStructOne one; // no pointer

那么前向声明在这个地方是行不通的。

如果我无法在任何地方使用前向声明怎么办?

那么你有一个设计问题。例如,如果不是 both DataStructOne* one;DataStructTwo* two;你有DataStructOne one;DataStructTwo two; ,那么这个数据结构在 C 或 C++ 中是不可实现的。您需要将其中一个字段更改为指针(在 C++ 中:智能指针),或者完全消除它。

关于c++ - 什么是循环包含依赖项,为什么不好,我该如何解决?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70350116/

相关文章:

c++ - 是否有一种代码预处理工具可以将多个用户定义的包含(无系统包含)合并到一个文件中?

c - Visual Studio 2010 错误# U1095,NMAKE

c++ - c++/cx 中的 storagefile::ReadAsync 异常?

c++ - 使用析构函数进行线程连接

C 编译错误

c - 结构体标签别名

c - 如何有选择地包含头文件

c++ - 来自 FBO 的延迟着色器纹理显示为黑色

c++ - VS 2008 C++ 构建输出?

c - 关于 vector 和函数的练习